在数组中查找具有最高属性值的对象的索引

14

我有一个包含对象的数组:

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

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

我不知道我的数组中会有多少个对象,但它们的属性值都不同,这里只是举了一些例子。

问题:

我需要找到一种方法来遍历所有这些对象,并检索拥有最高article.number值的对象的索引。如何实现?我只能使用javascript、jQuery等语言,不能使用其他语言。

我猜这将涉及到使用$.grep和Math.max,但我卡住了,我以前从未使用过$.grep。

简而言之:

var highestNumber = index of object where article.number is highest

3
你不知道如何编写循环?访问属性?比较值并存储更高的值?你尝试过什么,卡在哪里了? - user1106925
3
jQuery不是一种语言! - Bergi
1
如果你使用Underscore,在一条语句中是可能的。jQuery库没有数据工具。 - Bergi
1
你是想要索引还是只是包含最大值的对象?后者更容易。 - Alnitak
1
你需要迭代这些值来找到最大值,即使你使用一行jQuery代码,迭代仍然会在内部进行,所以本质上是相同的事情! - adeneo
显示剩余5条评论
7个回答

14

有许多方法可以做到这一点,包括Math.max()$.grep$.map等等,但一个易于理解的简单可读的方法是迭代对象并检查值是否大于某个变量,如果是,则将变量设置为更高的数字:

var highest = 0;

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

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

});

// highest now contains the highest number

2
注意:$.each 的执行速度相对较慢。手写的 whilefor 循环会更快。 - Alnitak
@Alnitak - 这只是为了简单起见,由于articles是一个对象数组,使用for循环会更快,但$.each在大多数情况下也可以正常工作。 - adeneo
我使用了 for 循环,因为我不知道它与其他方法在性能上的差异。同时,我添加了一行代码来跟踪对象的索引,这是我的主要目标。总的来说,这个答案对我帮助最大。 - Rijstkoek
1
最好将“highest”设置为键,因为这将是OP所需的索引。 - Xotic750

13

Underscore.js 是一个提供集合函数式操作的优秀库。下面是 Underscore 的解决方案:

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

虽然这个例子相当简单,但操作可以很好地扩展。假设你想要对数组中每个具有文本等于Foo和颜色等于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();  

如果您关心性能,使用外部库肯定是最好的选择。外部库可以提供大量优化,这些优化很难在自己的代码中实现。话虽如此,当处理少量数据(少于几千个)时,在这里发布的任何答案之间不会有明显的性能差异。不要过分担心基准测试,使用对您来说最易理解的答案。

JSFiddle


1
下划线是一个很棒的库,虽然我倾向于选择lodash,因为它有更好的优化。但出于兴趣,我确实将其添加到了性能测试中:http://jsperf.com/fastest-highest。不过,如果你有能力的话,自己动手做还是最好的。+1。 - Xotic750
4
如果你在使用 Lodash,_.maxBy 是一个易于使用的替代 ljfranklin 方法的选择。https://lodash.com/docs#maxBy - Defiant

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

如果你需要一个索引:

index = articles.indexOf(articleWithMaxNumber)

对于那些认为排序可能是获取最大值的过度操作的人:

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

以下是使用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 })

一些人担心使用sort方法比迭代方法“慢”,与此相关的IT技术。这里有一个fiddle,使用这两种方法处理了一个包含50000个元素的数组。在我的计算机上,sort方法比迭代方法“慢”约50毫秒。视应用而定,但在大多数情况下,这不是值得谈论的事情。

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>")


3
是的。这不够高效。虽然这是一个简单的解决方案,但你至少应该指出它可能不是最好的选择。 - Bergi
1
@Bergi:真的吗?从多少条目开始它才会变慢?与其他方法相比,它确切地慢了多少?您有任何数据来证明它是“不高效”的吗? - georg
3
排序算法的复杂度最好是O(n log n),而查找算法的复杂度为O(n)。肯定会有一个交叉点,但是Bergi是正确的,您不应该仅仅为了找到数组的最大值而对其进行排序。问题在于您无法提前知道这个交叉点在哪里,但是让一个基于排序的答案不受挑战也不是一个选项。 - Alnitak
2
@thg435,重点是“没有人知道”确切的数字,不过这个数字应该是“相对较低”的。 这取决于具体的实现和数据。对于小数据集,时间差异无关紧要,因此可以使用任何一种方法。对于大数据集,差异非常重要,因此请使用线性搜索。因此,如果数据集未知,请使用线性搜索。 - Alnitak
1
我喜欢通用方法的清晰度,但值得注意的是,您需要现代浏览器或适当的填充程序。此外,这些新方法往往很慢,因为它们没有像“while”或“for”那样进行过优化。 - Xotic750
显示剩余4条评论

1
items => 
    items
        .reduce(
            ( highest, item, index ) => 
                item > highest.item 
                    ? { item, index }
                    : highest
        ,   { item: Number.NEGATIVE_INFINITY }
        )
        .index

1
这是一个可能的解决方案。
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]);

jsfiddle

这里是当前给出方法的性能测试,请随意添加答案。


2
不要在数组上使用for-in循环,即使它使用hasOwnProperty进行了保护! - Bergi
哎呀,我不是故意的,我没有专心做我正在做的事情 :) 我在想如何迭代对象。 - Xotic750
typeof 测试的意义在哪里?如果输入数据正确,它永远不会失败。 - Alnitak
尝试在未定义时将数字与最高值进行比较。http://jsfiddle.net/Xotic750/CZLPJ/ - Xotic750
只需从 highest = 0i = 1 开始吗?不重复这个条件会更快... - Bergi
但是这种方法考虑了负数。只是我创建的数据集只有正数。 - Xotic750

0
array[array.map((o)=>o.number).indexOf(Math.max(...array.map((o)=>o.number)))]

意思是获取具有最高数字索引(i)的元素,其中(i)是索引。


0

我不会使用任何类似于Math或jQuery的东西,只需对结果数组进行排序并弹出最后一个元素:

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 !

只要你的记忆中没有海量文章,这是最快的方法。

@Xotic750:说真的,对于 howMany = 5,任何测量都是有缺陷的。无论你选择哪种方法,计算都在亚微秒内完成。此答案值得一赞,因为它提到了排序简单而短小,但如果你有数不清的文章,排序可能会相对较慢。 - Bergi
我更多地是指出,如果你要说某个东西是最快的,那么你应该准备用可衡量的东西来支持这种说法。顺便说一句,我没有投反对票。将其更改为50000。 - Xotic750

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接