14

I've got an array with objects:

var articles = [];
var article = {};

Simple loop that iterates x times {
        article.text = "foobar";
        article.color = "red";
        article.number = 5;
        articles.push(article);
}

I have no idea how many objects there will be in my array but they will all have different values for their properties, I just gave some examples here.

Question

I need to find a way to go through all these objects and retrieve the index of the object that has the highest value in article.number. How can I achieve this? I may only use javascript, jQuery, etc.. no other languages.

I'm guessing this will involve using both $.grep and Math.max but I'm stuck, I've never worked with $.grep before.

In short:

var highestNumber = index of object where article.number is highest
10
  • 3
    You don't know how to write a loop? To access a property? To compare values and store the higher? What have you tried, and where are you stuck? Commented May 12, 2013 at 15:46
  • 3
    jQuery is not a language! Commented May 12, 2013 at 15:58
  • 1
    It's possible in one statement if you use Underscore for example. The jQuery lib has not the tools for data. Commented May 12, 2013 at 16:11
  • 1
    Do you specifically want the index, or just the object that contains that max value? The latter is easier. Commented May 12, 2013 at 16:12
  • 1
    You'll have to iterate the values to find the highest one, even if you do it with one line of jQuery, the iteration still takes place internally, so it's the same thing! Commented May 12, 2013 at 16:15

7 Answers 7

14

There are many ways to do this, Math.max(), $.grep and $.map being a few, but an easy and readable method that should be understandable is to just iterate the object, and check if the value is higher than a variable, if it is, set the variable to the higher number :

var highest = 0;

$.each(articles, function(key, article) {

    if (article.number > highest) highest = article.number;

});

// highest now contains the highest number
Sign up to request clarification or add additional context in comments.

4 Comments

NB: $.each appears pretty slow. A manually written while or for loop will iterate much faster.
@Alnitak - it was just for simplicity, and as articles is an array of objects, a for loop would be faster as well, but $.each works fine for most cases.
I did go for a for loop because I didn't know there was a performance difference. Also added a line to keep track of the index of the object which was my primary goal, but overall this answer helped me the most.
It would probably have been better to set ´highest´ to key, as that would have been the index the OP was after.
13

Underscore.js is a wonderful library that provides functional operations for collections. A solution in underscore:

var maxObj = _.max(array, function (obj) {
  return obj.number;
});
var maxIndex = array.indexOf(maxObj);

While this example is fairly simple, the operations scale nicely. Say you wanted to sum the number property for each object in the array that had text equal to Foo and color equal to red:

var customSum = _.chain(array)
  .where({ 
    text: "Foo", color: "red"
  })
  .map(function(obj) {
    return obj.number; 
  })
  .reduce(function(memo, num) {
    return memo + num;
  }, 0)
  .value();  

If you're at all concerned with performance, an external library is certainly the way to go. There are a huge amount of optimizations that external libraries can provide that would be difficult to match in your own code. That said, when dealing with a small number of items (less than several thousand) there won't be an appreciable performance difference between any of the answers posted here. Don't sweat the benchmarking and use the answer that's the most understandable to you.

JSFiddle

2 Comments

underscore is a wonderful library, though I tend to choose lodash as it has better optimisations. But for interest sake I did add it to the performance tests: jsperf.com/fastest-highest Still, you can't beat doing it yourself, if you are competent. +1 from me
If you're using Lodash, _.maxBy is a easy to use replacement to ljfranklin's method. lodash.com/docs#maxBy
5

How about:

articleWithMaxNumber = articles.slice(0).sort(
     function(x, y) { return y.number - x.number })[0]

and if you need an index:

index = articles.indexOf(articleWithMaxNumber)

And for those thinking sorting might be an overkill to get the max value:

articleWithMaxNumber = articles.reduce(function(max, x) {
    return x.number > max.number ? x : max;
})

And here's a generic approach how to find a maximum of function applications using map-reduce:

function maxBy(array, fn) {
    return array.map(function(x) {
        return [x, fn(x)]
    }).reduce(function(max, x) {
        return x[1] > max[1] ? x : max;
    })[0]
}

articleWithMaxNumber = maxBy(articles, function(x) { return x.number })

Some people raised concerns about the sort method being "slow", compared to the iterative one. Here's a fiddle that uses both methods to process an array with 50000 items. The sort method is "slower" by about 50 milliseconds on my machine. Depends on the application, but in most cases this is not something worth talking about.

var articles = [];
var len = 50000;

while (len--) {
  var article = {};
  article.text = "foobar";
  article.color = "red";
  article.number = Math.random();
  articles.push(article);
}

d = performance.now();
max1 = articles.slice(0).sort(
  function(x, y) {
    return y.number - x.number
  })[0]
time1 = performance.now() - d

d = performance.now();
max2 = articles.reduce(function(max, x) {
  return x.number > max.number ? x : max;
})
time2 = performance.now() - d

document.body.innerHTML = [time1, time2].join("<br>")

9 Comments

Yes. It's unperformant. While a simple solution, you should at least indicate that it might not be the best.
@Bergi: Is it really? From which number of entries does it start to perform slowly? How much slower exactly is it compared to other methods? Do you have any figures at all to prove it being "unperformant"?
Sorting is at best O(n log n), whereas searching is O(n). There'll be a cross over point, sure, but Bergi is right, you should never sort an array just to find its maximum. The problem is, you don't know in advance where that cross over will be, but leaving a sort-based answer unchallenged is not an option.
@thg435 the point is, nobody knows exactly, although the number should be "relatively low". It's implementation and data dependent. For small data sets, the timing difference doesn't matter, so use either. For large data sets, the difference matters hugely, so use linear search. If the data set is unknown, therefore, use linear search.
I like the generic method for clarity, but it is worth noting that you need a modern browser or you will need the appropriate polyfills. Also these new methods tend to be slow as they have not had the kind of optimisation that ´while´ or ´for´ have
|
1

Here is one possible solution

Javascript

var articles = [],
    howMany = 5,
    i = 0,
    article,
    highest;

while (i < howMany) {
    article = {};
    article.text = "foobar";
    article.color = "red";
    article.number = i;
    articles.push(article);
    i += 1;
}

console.log(articles);

hownMany = articles.length;
i = 0;
while (i < howMany) {
    if (typeof highest !== "number" || articles[i].number > highest) {
        highest = i;
    }

    i += 1;
}

console.log(articles[highest]);

On jsfiddle

Here is the performance test for the current given methods, feel free to add answers.

6 Comments

Don't use for-in-loops on arrays, even if it's secured with hasOwnProperty!
Opps, I didn't mean too, was not concentrating on what I was doing :) I was thinking about iterating objects
what's the point of the typeof test? If the input data is correct, it'll never fail.
Try comparing the number against highest when it is undefined. jsfiddle.net/Xotic750/CZLPJ
Just start with highest = 0 and i = 1? Will be much faster not to repeat this condition…
|
1
items => 
    items
        .reduce(
            ( highest, item, index ) => 
                item > highest.item 
                    ? { item, index }
                    : highest
        ,   { item: Number.NEGATIVE_INFINITY }
        )
        .index

Comments

0

I won't use anything like Math or jQuery, just sort the resulting array and popping off the last element:

var sortArticles = function (a, b)
{
    return ( a.number - b.number ); // should have some type checks ? inverse order: swap a/b
}

articles.sort(sortArticles);


highestArticle = articles.pop(); // or articles[array.length-1]; 
// take care if srticles.length = null !

As long as you don't have gazillions of articles in your memory, that's the fastest way.

2 Comments

@Xotic750: Seriously, for howMany = 5 any measurement is flawed. Regardless which method you choose, the computation is done in sub-microseconds. And +1 for this answer as it mentions that sorting is simple and short but can be reasonable too slow if you have gazillions of articles.
I was more pointing out the fact that if you are going to state something is fastest then you should be prepared to backup that claim with something measurable. And btw, I didn't down vote. Changed it to 50000
0
array[array.map((o)=>o.number).indexOf(Math.max(...array.map((o)=>o.number)))]

Means get element with index (i) where (i) is the index of highest number .

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.