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