Smith Smithy
Smith Smithy

Reputation: 585

find closest lower "key" in javascript object

I am working with a javascript object and although I have a solution I cant help but think it can be done more efficiently.

the object is returned from an ajax call to php script

r.price_array[1] = 39.99
r.price_array[5] = 24.99
r.price_array[10] = 19.99
and so on....

what i am doing now is searching between the key values (key values represent a quantity)

qty = $(this).val();

if (qty >= 1 && qty <= 4){
    price_set = 1;
}
else if (qty >= 5 && qty <= 9){
    price_set = 15;
}
else if (qty >= 10 && qty <= 14){
    price_set = 25;
}

//and so on...

console.log(r.price_array[price_set]); //this returns the value

is there a way to take a quantity of 3 and find the next lowest key match which would be 1? or quantity of 7 and find key 5?

Upvotes: 4

Views: 3462

Answers (6)

Kentin
Kentin

Reputation: 19

Another solution is to use a for-in loop. It has the benefice to break once the value is found.

/**
 * @param {object} object - object with sorted integer keys
 * @param {number} index - index to look up
 */
function getValueForLowestKey(object, index) {
    let returned;

    for (const key in object) {
        if(key > index) break;
        returned = object[key];
    }

    return returned;
}

Upvotes: 1

Scott Sauyet
Scott Sauyet

Reputation: 50797

Another approach, demonstrated in this Fiddle:

var lowerKeyFinder = function(prices) {
    var keys = Object.keys(prices);
    keys.sort(function(a, b) {return a - b;});

    return function(val) {
        var maxKey = -1;
        for (var i = 0, len = keys.length; i < len; i++) {
            if (maxKey < 0 || keys[i] < val) {
                maxKey = Math.max(maxKey, keys[i]);
            }
        }
        return maxKey;
    };
};

var lookup = lowerKeyFinder(r.price_array);

lookup(3);  //=> 1
lookup(7);  //=> 5

This does not insist that the keys are initially presented in order, but sorts them once. It builds on the answer from @h2ooooooo but works a bit differently as it just offers in the end a simple function to look up by quantity.

Upvotes: 2

Paul
Paul

Reputation: 141837

Here's a function to do this. It expects to be used on objects with numeric keys like your array:

function getClosestKey(arr, target, u){
  if(arr.hasOwnProperty(target))
    return target;

  var keys = Object.keys(arr);
  keys.sort(function(a,b){ return a-b; });

  // Can replace linear scan with Binary search for O(log n) search
  // If you have a lot of keys that may be worthwhile
  for(var i = 0, prev; i < keys.length; i++){
    if(keys[i] > target)
      return prev === u ? u : +prev;
    prev = keys[i];
  }
  return +keys[i - 1];
}

You'll have to SHIM Object.keys in older browsers:

Object.keys = Object.keys || function(obj){
  var result = [];
  for(var key in obj)
    if(obj.hasOwnProperty(key)) result.push(key);
  return result;
}

Usage:

var price_array = [];
price_array[1] = 39.99;
price_array[5] = 24.99;
price_array[10] = 19.99;

getClosestKey(price_array, 0); // undefined
getClosestKey(price_array, 1); // 1
getClosestKey(price_array, 3); // 1
getClosestKey(price_array, 4); // 1
getClosestKey(price_array, 5); // 5
getClosestKey(price_array, 7); // 5
getClosestKey(price_array, 9); // 5
getClosestKey(price_array, 10); // 10
getClosestKey(price_array, 100); // 10

Upvotes: 0

fred02138
fred02138

Reputation: 3361

My version (tested, fiddle is here: http://jsfiddle.net/fred02138/UZTbJ/):

// assume keys in rprice object are sorted integers
function x(rprice, qty) {
    var prev = -1;
    var i;
    for (i in rprice) {
        var n = parseInt(i);
        if ((prev != -1) && (qty < n))
            return prev;
        else 
            prev = n;
    }    
}

var rprice = {
    1: 39.99,
    5: 24.99,
    10: 19.99
}

alert(x(rprice, 3));
alert(x(rprice, 7));

Upvotes: 2

h2ooooooo
h2ooooooo

Reputation: 39542

Sure - just use a loop:

var price_array = {
    1: 39.99,
    5: 24.99,
   10: 19.99,
   15: 15.99,
   20: 10.99,
   25:  5.99,
   30:  0.99
}
var qty = 12;

var maxKey = -1;
for (var key in price_array) {
    if (maxKey < 0 || key < qty) {
        maxKey = Math.max(maxKey, key);
    }
}
console.log(maxKey); //10
console.log(price_array[maxKey]); //19.99

Upvotes: 2

leepowers
leepowers

Reputation: 38318

You can round to the nearest multiple of 5 using Math.round() and some simple division:

var price_array = {};
price_array[1] = 39.99;
price_array[5] = 24.99;
price_array[10] = 19.99;

function qty_round(qty) {
  // Math.max() is used to enforce a minimum quantity of 1
  return Math.max(1, 5 * Math.round(qty / 5));
}

console.log(price_array[qty_round(1)]);  // 39.99
console.log(price_array[qty_round(4)]);  // 24.99
console.log(price_array[qty_round(9)]);  // 19.99
console.log(price_array[qty_round(10)]); // 19.99

With some minor modifications you could round down instead of up (using Math.floor instead of Math.round) or enforce a maximum quantity.

Upvotes: 0

Related Questions