1

I have a object with data like this:

var data = {
    '4': [1, 2, 3],
    '5': [1, 2, 3],
    '6': [1, 2, 3],
    '7': [1, 2, 3],
    '8': [1, 2, 3],
    '9': [1, 2, 3],
    '10': [1, 2, 3],
    '11': [1, 2, 3],
    '12': [1, 2, 3],
    '15': [1, 9, 3],
    '18': [1, 2, 3],
    '21': [1, 8, 3],
    '24': [1, 2, 3],
    '30': [1, 2, 3],
    '36': [1, 2, 3],
    '42': [1, 20, 3]
}

Now I want to access the data like

var result = data[i][1];

This would give me a result of 9 if i = 15.

But I need to get always the next lower index, if the given index isn't existing. So, if i = 16 the result should also be 9. If i = 23 the result is 8 and for i = 999, the result is 20. How can I do that?

11
  • Is there any reason you use an object with numerical indexes instead of an array? Commented Oct 12, 2015 at 19:19
  • How is the object populated? Is it ever changed after first assignment? Commented Oct 12, 2015 at 19:22
  • 1
    First of all, i is not an index, it's a property name, or a key, if you will. Don't use object like arrays. It might be more prudent to ask why you want to achieve the fall-down logic, and how sparse you dataset is going to be, before deciding on the best representation of that data. Commented Oct 12, 2015 at 19:29
  • @JNF No, the object won't be changed at all. Commented Oct 12, 2015 at 19:29
  • 1
    Sort Object.keys(data) (use a compare function that sorts those indexes for you), and then use a simple search algorithm where you return the key equal or smaller to your search value, starting with the last index of the sorted array. Commented Oct 12, 2015 at 19:43

5 Answers 5

4

You're going to have to loop downwards searching for that property in your array. Assuming input is the one you're trying to find,

for (var i = input; i > 0; i--) { // Looping incrementing down
    if (data.hasOwnProperty(i)) {
        result = data[i][1];
        break;
    }
}

The hasOwnProperty method checks if your data array has that index available, and breaks out of the loop after setting result if it does.

Sign up to request clarification or add additional context in comments.

2 Comments

I think that by "Incrementing down" you mean "decrementing".
Ahahaha yes. My thought process is sometimes odd.
0

For a valid key I would suggest

  1. try if the key exists, or
  2. get all keys, map them to Number, perform a numerical sort and at least reduce the data to a key, which is smaller than the next bigger value.

var data = { '4': [1, 2, 3], '5': [1, 2, 3], '6': [1, 2, 3], '7': [1, 2, 3], '8': [1, 2, 3], '9': [1, 2, 3], '10': [1, 2, 3], '11': [1, 2, 3], '12': [1, 2, 3], '15': [1, 9, 3], '18': [1, 2, 3], '21': [1, 8, 3], '24': [1, 2, 3], '30': [1, 2, 3], '36': [1, 2, 3], '42': [1, 20, 3] };

function getKey(key) {
    return key in data ? key : Object.keys(data).map(Number).sort(function (a, b) { return a - b; }).reduce(function (r, a) {
        return a <= key ? a : r;
    });
}

document.write('3 ' + getKey(3) + '<br>');
document.write('15 ' + getKey(15) + '<br>');
document.write('16 ' + getKey(16) + '<br>');
document.write('23 ' + getKey(23) + '<br>');
document.write('999 ' + getKey(999) + '<br>');

Edit: For even better performance, avoids inspection of all items in the keys array. The solution works for sparse arrays as well, like

var data = [];
data[4] = [1, 2, 3];
data[5] = [1, 2, 3];
data[6] = [1, 2, 3];

var data = { '4': [1, 2, 3], '5': [1, 2, 3], '6': [1, 2, 3], '7': [1, 2, 3], '8': [1, 2, 3], '9': [1, 2, 3], '10': [1, 2, 3], '11': [1, 2, 3], '12': [1, 2, 3], '15': [1, 9, 3], '18': [1, 2, 3], '21': [1, 8, 3], '24': [1, 2, 3], '30': [1, 2, 3], '36': [1, 2, 3], '42': [1, 20, 3] },
    keys = Object.keys(data).map(Number).sort(function (a, b) { return a - b; }),
    i;

function getKey(key) {
    var lower = 0,
        upper = keys.length - 1,
        index;

    if (key in data) {
        return key;
    }
    if (key < keys[0]) {
        return; // this is not specified in question
    }
    if (key > keys[upper]) {
        return keys[upper];
    }
    while (lower !== upper) {
        index = lower + upper >> 1;
        if (key > keys[index]) {
            lower = index + 1;
            continue;
        }
        upper = index;
    }
    return keys[lower - 1];
}

for (i = -5; i < 50; i++) {
    document.write('value: '+ i + ', key: ' + getKey(i) + '<br>');
}

1 Comment

your first approach already should cover the sparse array case. Object.keys as well as map and filter are implemented in a way that will skip omitted index based keys for arrays.
0

You need to look for the index request, and if you don't find it, subtract one from the index and try again. Something like:

function getItem(i,j) {
    if (i < 0) {
        // assuming no negative keys exists
        return null;   // or throw an error if you prefer
    }
    if (data[i]) {
        return data[i][j];
    }
    else {
         return getItem(i-1,j);
    }
}

Usage:

getItem(16,1);    // should give you 9

Comments

0

To round it to the closest number ether way, try using this:

var data = {
    '4': [ 1, 2, 3 ],
    '5': [ 1, 2, 3 ],
    '6': [ 1, 2, 3 ],
    '7': [ 1, 2, 3 ],
    '8': [ 1, 2, 3 ],
    '9': [ 1, 2, 3 ],
    '10': [ 1, 2, 3 ],
    '11': [ 1, 2, 3 ],
    '12': [ 1, 2, 3 ],
    '15': [ 1, 9, 3 ],
    '18': [ 1, 2, 3 ],
    '21': [ 1, 8, 3 ],
    '24': [ 1, 2, 3 ],
    '30': [ 1, 2, 3 ],
    '36': [ 1, 2, 3 ],
    '42': [ 1, 20, 3 ]
}

var keys = $.map( data, function ( element, index ) {
    return index
} );

function closest( number ) {
    var closest = null;
    $.each( keys, function () {
        if ( closest == null || Math.abs( this - number ) < Math.abs( closest - number ) ) {
            closest = this;
        }
    } );
    return closest;
}

console.log( data[closest( 16 )][1] );

Thanks to: https://stackoverflow.com/a/3561328/5414240 for the closest function.

Hope this helps.

Comments

0

If the data-structure is similar to a sparse array and if the indices of this data-structure tend to be bigger integer representatives, then a "try-and-error count-down approach" with stepwise decreasing a given index by 1 might not perform anymore that well.

The following example tries to take this into account ...

var getNextLowerOrSameIndex = function (obj, idx) {
  var
    indexCount,
    listOfIndices
  ;
  idx = parseInt(idx, 10);

  if (!(idx in obj)) {
    listOfIndices = Object.keys(obj);

    idx = (listOfIndices.every(function (index, count/*, listOfIndices*/) {

      indexCount = count;
      index = parseInt(index, 10);

      listOfIndices[indexCount] = index;

      return (idx > index);

    }) && Math.max.apply(null, listOfIndices)) || listOfIndices[indexCount - 1];
  }
//return idx;
  return (Number.isFinite(idx) && idx) || (void 0);
};

...

var data = {
  '4': [1, 2, 3],
  '5': [1, 2, 3],
  '6': [1, 2, 3],
  '7': [1, 2, 3],
  '8': [1, 2, 3],
  '9': [1, 2, 3],
  '10': [1, 2, 3],
  '11': [1, 2, 3],
  '12': [1, 2, 3],
  '15': [1, 9, 3],
  '18': [1, 2, 3],
  '21': [1, 8, 3],
  '24': [1, 2, 3],
  '30': [1, 2, 3],
  '36': [1, 2, 3],
  '42': [1, 20, 3]
};

console.log(data[getNextLowerOrSameIndex(data, 4)][1]);     // 2
console.log(data[getNextLowerOrSameIndex(data, "4")][1]);   // 2
console.log(data[getNextLowerOrSameIndex(data, "5")][1]);   // 2

console.log(data[getNextLowerOrSameIndex(data, 15)][1]);    // 9
console.log(data[getNextLowerOrSameIndex(data, 16)][1]);    // 9
console.log(data[getNextLowerOrSameIndex(data, "15")][1]);  // 9
console.log(data[getNextLowerOrSameIndex(data, "17")][1]);  // 9

console.log(data[getNextLowerOrSameIndex(data, "23")][1]);  // 8
console.log(data[getNextLowerOrSameIndex(data, 999)][1]);   // 20

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.