efgdh
efgdh

Reputation: 345

Javascript find the closest number in an array of objects and retrieve the object's key value

I have an array of objects(with keys: name, quoteNumber)and I would like to find the closest quoteNumber that is smaller than a given number then retrieve that object's name, I have consider using a for loop to remove the larger values, and get the max value from the remaining ones ,yet it might not be the best option given how large the dataset would be. Is this any other algorithm that is more efficient? Thanks!

const givenNum = 45
var array = [ 

    { name: "Sally",
      quoteNumber: 35},
    { name: "Jane",
      quoteNumber: 20},
    { name: "Edwin",
      quoteNumber: 55},
    { name: "Carrie",
      quoteNumber: 47}];

//'result:' Sally

Upvotes: 2

Views: 402

Answers (4)

Ben Stephens
Ben Stephens

Reputation: 3371

This is based on @Jeremy Roman's and @Jamiec's answers. I think this is a good bit faster unless I've done something stupid: https://jsbench.me/g0kmha8buo/1

const array = [
  { name: "Sally",
    quoteNumber: 35},
  { name: "Jane",
    quoteNumber: 20},
  { name: "Velma",
    quoteNumber: 31},
  { name: "Edwin",
    quoteNumber: 55},
  { name: "Neva",
    quoteNumber: 30},
  { name: "Carrie",
    quoteNumber: 47},
  { name: "Arnold",
    quoteNumber: 29},
];

function closest_quote_less_than_or_equal_to(quotes, limit) {
  let winner = null;
  let winningQuote = 0;

  const limit_plus_one = limit + 1;

  for(let i = 0; i < quotes.length; i++) {
    const quoteNumber = quotes[i].quoteNumber;
    if(((quoteNumber < limit_plus_one) * quoteNumber) > winningQuote) {
      winningQuote = quoteNumber;
      winner = quotes[i].name;
    }
  }

  return winner;
}

console.log(closest_quote_less_than_or_equal_to(array, 45));
console.log(closest_quote_less_than_or_equal_to(array, 30));
console.log(closest_quote_less_than_or_equal_to(array, 10));

Upvotes: 0

Jamiec
Jamiec

Reputation: 136074

If you have a large dataset, you want to avoid doing anything which either loops over the array more than once, or even attempts to sort it before looking. A simple aggregate operation will do this fine. Use reduce

const givenNum = 45
var array = [ 

    { name: "Sally",
      quoteNumber: 35},
    { name: "Jane",
      quoteNumber: 20},
    { name: "Edwin",
      quoteNumber: 55},
    { name: "Carrie",
      quoteNumber: 47}];
      

const result = array.reduce ( (acc,item) => {
    const diff = givenNum - item.quoteNumber;
    if(item.quoteNumber < givenNum && diff < acc.diff)
       acc = {diff,  item}
    return acc;
},{ diff: Number.MAX_SAFE_INTEGER, item: null });

console.log(result.item);

Note also that if speed is really important avoid this solution too - it has extra method calls you won't have with with the simpler loop solution. That will always be the fastest option.

Upvotes: 4

Jeremy Roman
Jeremy Roman

Reputation: 16345

If it's unsorted, about the most efficient you can be is a single pass.

function getHighestQuote(quotes, limit) {
  let winner = null;
  let winningQuote = null;
  for (let {name, quoteNumber} of quotes) {
    if (quoteNumber > limit)
      continue;
    if (winningQuote === null || winningQuote < quoteNumber) {
      winner = name;
      winningQuote = quoteNumber;
    }
  }
  return winner;
}

It's not quite as snazzy as a functional approach, but it's a single linear-time pass that only needs to allocate a few stack variables.

Upvotes: 6

andi2.2
andi2.2

Reputation: 134

I've made a simple one-liner that kicks out all elements with a larger quote-number, sorts the other elements and finally get's the first element(-> the closest one):

const closest_smaller = array.filter(a => a.quoteNumber <= givenNum).sort((a,b) => b.quoteNumber-a.quoteNumber)[0]

If the givenNum is smaller than every quoteNumber, this returns undefined. if you want to have a fallback use:

const closest_smaller = array.filter(a => a.quoteNumber <= givenNum).sort((a,b) => b.quoteNumber-a.quoteNumber)[0] ?? 0

You also can use find instead of filter, i don't exactly know which one is faster:

const closest_smaller = array.sort((a,b) => b.quoteNumber-a.quoteNumber).find(a => a.quoteNumber <= givenNum) ?? 0

Upvotes: 0

Related Questions