在 JavaScript 对象数组中通过 id 查找对象

2117

我有一个数组:

myArray = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'}, etc.]

我无法更改数组的结构。我正在传递一个id参数 45,我想获取该数组中该对象的'bar'值。

如何在JavaScript或使用jQuery实现?

37个回答

2158

使用find()方法:

myArray.find(x => x.id === '45').foo;

来自MDN:

find()方法返回数组中满足提供的测试函数的第一个元素。否则会返回undefined


如果你想要找到它的索引,可以使用findIndex()方法:

myArray.findIndex(x => x.id === '45');

来自MDN

findIndex()方法返回数组中第一个满足所提供测试函数的元素的索引。否则返回-1。


如果你想要获取匹配元素的数组,请使用filter()方法:

myArray.filter(x => x.id === '45');

这将返回一个对象数组。如果你想要获取一个由 foo 属性组成的数组,你可以使用 map() 方法来实现:

myArray.filter(x => x.id === '45').map(x => x.foo);

顺带一提:像 find()filter() 这样的方法,以及 箭头函数 不受旧版浏览器(如 IE)的支持,因此,如果您想要支持这些浏览器,应该使用 Babel (带 polyfill)对代码进行转译。


7
针对多重测试条件,代码可能类似于:myArray.find(x => x.id === '45' && x.color == 'red').foo - Apqu
5
到目前为止,对我来说是最好的答案。不需要使用jQuery,也不需要创建新的辅助数组。 - Canta
1
筛选器实际上支持IE9! - Leland
10
如果没有id为'45'的对象,myArray.find(x => x.id === '45').foo;会抛出异常。 - Frazer Kirkman
2
我可以在find方法中添加多个条件吗? - Si8
显示剩余8条评论

1498

既然您已经在使用jQuery,那么可以使用 grep 函数来搜索数组:

var result = $.grep(myArray, function(e){ return e.id == id; });

结果是一个包含找到的项目的数组。如果你知道对象始终存在且仅出现一次,那么你可以使用 result[0].foo 来获取值。否则,您应该检查结果数组的长度。例如:

if (result.length === 0) {
  // no result found
} else if (result.length === 1) {
  // property found, access the foo property using result[0].foo
} else {
  // multiple items found
}

136
为避免 JavaScript 中 == 操作符的奇怪问题,最好使用 ===,两者的差别在于前者比较严格。 - Vicky Chijwani
11
与字符串比较字符串是否存在任何问题? - Guffa
42
如果你完全确定e.idid都是字符串,那么使用==可能是可以的。但如果你不确定,你可能会遇到问题(因为'' == 0true'' === 0false)。更不用说===似乎更快(http://stackoverflow.com/questions/359494/javascript-vs-does-it-matter-which-equal-operator-i-use#comment14900957_359494)。 - Vicky Chijwani
110
基本上,我总是使用 ===,因为它与其他编程语言中的 == 完全 相同。在 JavaScript 中,我认为 == 是不存在的。 - Vicky Chijwani
6
这里有很多回答提供了查找唯一值时的预期行为; 你可以通过它们尽早返回或从循环中退出(或指示低级别结构停止迭代)来识别它们。请参考JaredPar的答案作为标准示例,以及Aaronius对该答案的评论以获取相同的见解。通常,人们以这种方式区分“过滤”和“查找”函数,但术语会有所不同。虽然更有效率,但这仍然是一个线性搜索,如果想使用哈希表,请参考Aaron Digulla的答案(注意实现细节)。 - tne
显示剩余8条评论

381

另一个解决方案是创建一个查找对象:

var lookup = {};
for (var i = 0, len = array.length; i < len; i++) {
    lookup[array[i].id] = array[i];
}

... now you can use lookup[id]...

如果您需要进行多次查找,这将特别有趣。

由于ID和对象将被共享,因此这不需要更多的内存。


6
正是我想要的。有趣的是,我曾试图通过循环遍历每个元素,并在找到每个元素时从列表中删除它来过度复杂化它,而我只需要改变从CouchDB接收到的数据并将其转换为对我有用的格式即可。加一分! - slickplaid
5
这很聪明。我无法想象其他人是如何通过在每次使用时搜索整个数组来说服自己的。 - Aladdin Mhemed
4
只要您不依赖于属性的顺序:https://dev59.com/8G445IYBdhLWcg3wZJUn - Martin Dandanell
如果您知道只有一个对象需要查找,那么在循环中使用 break; 是一个好的选择/改进吗? - irJvV
8
不,这完全没有意义。如果您只需要查找一次,则上面的代码是没有用的。创建一个“lookup”对象是浪费时间的。 - Aaron Digulla
显示剩余9条评论

232

ECMAScript 2015(JavaScript ES6)在数组上提供了 find() 方法:

var myArray = [
 {id:1, name:"bob"},
 {id:2, name:"dan"},
 {id:3, name:"barb"},
]

// grab the Array item which matchs the id "2"
var item = myArray.find(item => item.id === 2);

// print
console.log(item.name);

它可以在不使用外部库的情况下工作。但是,如果你想要支持旧浏览器,你可能需要包含此 Polyfill


1
可能是因为它仍然看起来非常实验性,而且没有多少浏览器支持它。 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/find - lejonl
2
这可以简化为 myArray.find(d=>d.id===45).foo; - Shaggy
2
@Shaggy 或者甚至是 myArray.find(({ id }) => id === 45).foo。但这是一个旧的答案,写于ES2015语法得到充分支持之前。@Gothdo的答案目前是该线程中最新的。 - Rúnar Berg
1
@Shaggy 如果 .find() 返回 undefined,那么你的优化会抛出一个错误。因此,这个解决方案只能在匹配是保证的情况下使用。 - Herbert Peters
1
如果你想要确保,你可以随时进行空值检查,这在使用可选链后将非常容易:myArray.find(d => d.id === 45)?.foo - Rúnar Berg
显示剩余2条评论

147

Underscore.js有一个很好的方法可以实现这个:

myArray = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'},etc.]
obj = _.find(myArray, function(obj) { return obj.id == '45' })

44
记录一下,Lo-Dash(通常比Underscore具有更好的性能)有一个类似的方法。文档在这里:http://lodash.com/docs#find - user456584
如果你只期望一个对象,那么使用findWhere会更高效,因为在找到一个结果后,搜索不会继续进行。 - Foreever
从_.find的文档中可以看到:“该函数在找到一个可接受的元素后立即返回,而不会遍历整个列表。” - GijsjanB

143

我认为最简单的方法如下,但它不适用于Internet Explorer 8(或更早版本):

var result = myArray.filter(function(v) {
    return v.id === '45'; // Filter out the appropriate one
})[0].foo; // Get result and access the foo property

我很好奇,这里与通常的“for”相比是否有性能优势? - Igor Zinov'yev
@Igor Zinov'yev:是的,使用ES5数组工具肯定会有性能影响。对于每个元素,都会执行一个单独的函数,因此与直接使用“for”循环相比,它不会真正快速。 - pimvdb
那么你的意思是它会更慢吗?而且据我所见,它将始终扫描整个数组,而“for”循环将在第一次匹配时终止。 - Igor Zinov'yev
如果您需要支持IE8,只需将此内容放入其中:https://dev59.com/WWw05IYBdhLWcg3wmCwo - Adam Grant
如果没有该“id”的元素,此代码将引发错误。 - Stan

81
尝试以下内容:
function findById(source, id) {
  for (var i = 0; i < source.length; i++) {
    if (source[i].id === id) {
      return source[i];
    }
  }
  throw "Couldn't find object with id: " + id;
}

19
在现代浏览器中,这个解决方案可以写成:http://jsfiddle.net/rwaldron/j3vST/,虽然这并不值得单独回答。 - Rick
13
如果你想要提高效率,注意这个例子可能比使用filter()更快(参见Rick的例子),因为它一旦找到第一个匹配项就会返回,而filter()会在找到匹配项后继续运行整个数组。此外,这个例子也没有创建额外数组或对每个项调用函数的成本。 - Aaronius
3
@Rick,这个回答最有意思的事情显然是你可以将Firebug控制台添加到jsFiddle的输出窗口中。 这比记录日志并告诉别人打开控制台查看输出要好得多。真棒! - KyleMit
2
由于迄今为止没有人提到,我想补充一下AngularJS也有一个filter方法。 - Eno
@JaredPar https://dev59.com/e6Lia4cB1Zd3GeqPlZeI - Valay

48
myArray.filter(function(a){ return a.id == some_id_you_want })[0]

旧版浏览器的Polyfill:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#Polyfill - Justin
@Danilo,我该如何在嵌套对象中进行搜索?https://dev59.com/e6Lia4cB1Zd3GeqPlZeI - Valay

36

一个通用且更加灵活的findById函数版本:

// array = [{key:value},{key:value}]
function objectFindByKey(array, key, value) {
    for (var i = 0; i < array.length; i++) {
        if (array[i][key] === value) {
            return array[i];
        }
    }
    return null;
}

var array = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'}];
var result_obj = objectFindByKey(array, 'id', '45');

36

性能

今天2020.06.20,我在Chrome 81.0、Firefox 77.0和Safari 13.1上对选择的解决方案进行了MacOs High Sierra测试。

使用预计算的解决方案结论

使用预计算的解决方案(K,L)比其他解决方案(快得多得多)快,并且不会与它们进行比较 - 可能它们使用了一些特殊的内置浏览器优化。

  • 令人惊讶的是,在 Chrome 和 Safari 上,基于Map (K) 的解决方案比基于对象{} (L) 的解决方案更快。
  • 令人惊讶的是,在 Safari 上,对于小数组,基于对象 {} (L) 的解决方案比传统的for (E) 更慢。
  • 令人惊讶的是,在 Firefox 上,对于小数组,基于Map (K) 的解决方案比传统的for (E) 更慢。

当搜索对象始终存在时的结论

  • 对于小数组,使用传统的for (E) 的解决方案是最快的,对于大数组也很快。
  • 使用缓存 (J) 的解决方案对于大数组是最快的 - 令人惊讶的是,对于小数组来说速度也很快。
  • 基于find (A) 和findIndex (B) 的解决方案在小数组上很快,在大数组上速度中等。
  • 基于$.map (H) 的解决方案在小数组上最慢。
  • 基于reduce (D) 的解决方案在大数组上最慢。

enter image description here

当搜索对象从不存在时的结论

  • 基于传统for(E)的解决方案在小型和大型数组上最快(除了Chrome小数组,其中它是第二快的)
  • 基于reduce(D)的解决方案在大型数组上最慢
  • 使用缓存的解决方案(J)速度中等,但如果我们在缓存中保存具有空值的键,则可以加快速度(这里没有这样做,因为我们想避免在搜索许多不存在的键时缓存中的无限内存消耗)

enter image description here

详情

针对以下解决方案:

  • 无需预先计算:A B C D E F G H I J(J解决方案使用“内部”缓存,其速度取决于搜索元素的重复频率)
  • 预先计算: K L

我进行了四项测试。在这些测试中,我想在10个循环迭代中找到5个对象(对象ID在迭代过程中不变)- 因此我调用被测试的方法50次,但只有前5次具有唯一的ID值:

  • 小数组(10个元素)和搜索对象始终存在 - 您可以在这里执行它
  • 大数组(10k个元素)和搜索对象始终存在 - 您可以在这里执行它
  • 小数组(10个元素)和搜索对象从不存在 - 您可以在这里执行它
  • 大数组(10k个元素)和搜索对象从不存在 - 您可以在这里执行它

测试代码如下所示

function A(arr, id) {
  return arr.find(o=> o.id==id);
}

function B(arr, id) {
  let idx= arr.findIndex(o=> o.id==id);
  return arr[idx];
}

function C(arr, id) {
  return arr.filter(o=> o.id==id)[0];
}

function D(arr, id) {
  return arr.reduce((a, b) => (a.id==id && a) || (b.id == id && b));
}

function E(arr, id) {
  for (var i = 0; i < arr.length; i++) if (arr[i].id==id) return arr[i];
  return null;
}

function F(arr, id) {
  var retObj ={};
  $.each(arr, (index, obj) => {
    if (obj.id == id) { 
      retObj = obj;
      return false;
    }
  });
  return retObj;
}

function G(arr, id) {
  return $.grep(arr, e=> e.id == id )[0];
}

function H(arr, id) {
  return $.map(myArray, function(val) {
    return val.id == id ? val : null;
  })[0];
}

function I(arr, id) {
  return _.find(arr, o => o.id==id);
}

let J = (()=>{
  let cache = new Map();
  return function J(arr,id,el=null) { 
    return cache.get(id) || (el=arr.find(o=> o.id==id), cache.set(id,el), el);
  }
})();

function K(arr, id) {
  return mapK.get(id)
}

function L(arr, id) {
  return mapL[id];
}



// -------------
// TEST
// -------------

console.log('Find id=5');

myArray = [...Array(10)].map((x,i)=> ({'id':`${i}`, 'foo':`bar_${i}`}));
const mapK = new Map( myArray.map(el => [el.id, el]) );
const mapL = {}; myArray.forEach(el => mapL[el.id]=el);



[A,B,C,D,E,F,G,H,I,J,K,L].forEach(f=> console.log(`${f.name}: ${JSON.stringify(f(myArray, '5'))}`));

console.log('Whole array',JSON.stringify(myArray));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

This snippet only presents tested codes

在 Chrome 中对小型数组进行搜索,且待搜索的对象总是存在的示例测试结果

enter image description here


这应该是正确的答案。当涉及到循环时,性能应该是一个重要的标准。 - Mojo Allmighty

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