在 JavaScript 对象数组中按 id 查找对象

Posted

技术标签:

【中文标题】在 JavaScript 对象数组中按 id 查找对象【英文标题】:Find object by id in an array of JavaScript objects 【发布时间】:2021-05-22 01:31:19 【问题描述】:

我有一个数组:

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

我无法更改数组的结构。我被传递了一个45 的ID,我想为数组中的那个对象获取'bar'

如何在 javascript 或使用 jQuery 中做到这一点?

【问题讨论】:

【参考方案1】:

正如其他人指出的那样,.find() 是在数组中查找一个对象时要走的路。但是,如果使用此方法无法找到您的对象,您的程序将崩溃:

const myArray = ['id':'73','foo':'bar','id':'45','foo':'bar'];
const res = myArray.find(x => x.id === '100').foo; // Uh oh!
/*
Error:
"Uncaught TypeError: Cannot read property 'foo' of undefined"
or in newer chrome versions:
Uncaught TypeError: Cannot read properties of undefined (reading 'foo')
*/

这可以通过在使用.foo 之前检查.find() 的结果是否已定义来解决。现代 JS 允许我们使用 optional chaining 轻松完成此操作,如果找不到对象,则返回 undefined,而不是让您的代码崩溃:

const myArray = ['id':'73','foo':'bar','id':'45','foo':'bar'];
const res = myArray.find(x => x.id === '100')?.foo; // No error!
console.log(res); // undefined when the object cannot be found

【讨论】:

【参考方案2】:

如果你多次这样做,你可以设置一个 Map (ES6):

const map = new Map( myArray.map(el => [el.id, el]) );

然后您可以简单地进行 O(1) 查找:

map.get(27).foo

【讨论】:

【参考方案3】:

使用find() 方法:

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

来自MDN:

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


如果您想找到它的 index,请使用 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);

旁注:旧版浏览器(如 IE)不支持 find()filter() 和 arrow functions 等方法,因此如果您想支持这些浏览器,您应该使用 Babel 转译您的代码( polyfill)。

【讨论】:

对于多个测试条件,它会因此类似于:myArray.find(x => x.id === '45' && x.color == 'red').foo 对我来说,迄今为止最好的答案。不需要 jQuery,也不需要创建新的辅助数组。 过滤器其实已经支持回IE9了! myArray.find(x => x.id === '45').foo;如果没有 id 为 '45' 的对象,则抛出异常。 我可以在find方法中添加多个条件吗?【参考方案4】:

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

它可以在没有外部库的情况下工作。但是如果你想要older browser support,你可能想要包含this polyfill。

【讨论】:

可能是因为它看起来还很实验性,而且没有多少浏览器支持它,developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 这可以简化为myArray.find(d=>d.id===45).foo; @Shaggy 甚至myArray.find(( id ) => id === 45).foo。但这是在 ES2015 语法和现在一样受到支持之前编写的旧答案。 @Gothdo 的 answer 是当前线程中最新的。 @Shaggy 如果 .find() 返回 undefined,那么您的优化会引发错误。所以这个解决方案只能在保证匹配的情况下使用。 @HerbertPeters 如果您想确保始终可以进行空值检查,optional chaining 将非常容易:myArray.find(d => d.id === 45)?.foo【参考方案5】:

性能

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

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

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

令人惊讶的是,基于 Map (K) 的 Chrome 和 Safari 解决方案比基于对象 (L) 的解决方案快得多 令人惊讶的是,Safari 上基于对象 (L) 的小型阵列解决方案比传统的 for (E) 慢 令人惊讶的是,Firefox 上基于 Map (K) 的小型阵列解决方案比传统的 for (E) 慢

搜索对象总是存在时的结论

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

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

基于传统 for (E) 的解决方案在小型和大型阵列上最快(Chrome 小型阵列除外,它的速度第二快) 基于reduce (D) 的解决方案在大型阵列上最慢 使用缓存 (J) 的解决方案速度中等,但如果我们将具有空值的键也保存在缓存中,则可以加快速度(这里没有这样做,因为我们希望避免缓存中无限的内存消耗,以防万一将搜索现有的密钥)

详情

解决方案

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

我进行了四次测试。在测试中,我想在 10 次循环迭代中找到 5 个对象(对象 ID 在迭代期间不会改变) - 所以我调用了 50 次测试方法,但只有前 5 次具有唯一的 id 值:

小数组(10 个元素)和搜索的对象始终存在 - 您可以执行它HERE 大数组(10k 个元素)和搜索到的对象总是存在 - 你可以执行它HERE 小数组(10 个元素)和搜索的对象从不存在 - 你可以执行它HERE 大数组(10k 个元素)和搜索的对象从不存在 - 你可以执行它HERE

测试代码如下

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 对始终存在搜索对象的小数组的示例测试结果

【讨论】:

【参考方案6】:

动态缓存查找

在这个解决方案中,当我们搜索某个对象时,我们会将其保存在缓存中。这是“始终搜索解决方案”和“在预先计算中为每个对象创建哈希映射”之间的中间点。

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


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

let myArray = [...Array(100000)].map((x,i)=> ('id':`$i`, 'foo':`bar_$i`));

// example usage

console.log( cachedFind(myArray,'1234').foo );





// Benchmark

let bench = (id) => 
  console.time   ('time for '+id ); 
  console.log    ( cachedFind(myArray,id).foo );  // FIND
  console.timeEnd('time for '+id );


console.log('----- no cached -----');
bench(50000);
bench(79980);
bench(99990);
console.log('-----  cached   -----');
bench(79980); // cached
bench(99990); // cached

【讨论】:

【参考方案7】:

我们可以使用Jquery方法$.each()/$.grep()

var data= [];
$.each(array,function(i)if(n !== 5 && i > 4)data.push(item)

var data = $.grep(array, function( n, i ) 
  return ( n !== 5 && i > 4 );
);

使用 ES6 语法:

Array.find, Array.filter, Array.forEach, Array.map

或者使用 Lodash https://lodash.com/docs/4.17.10#filter,下划线 https://underscorejs.org/#filter

【讨论】:

【参考方案8】:

最近,我不得不面对同样的事情,我需要从一个巨大的数组中搜索字符串。

经过一番搜索,我发现用简单的代码很容易处理:

代码:

var items = mydata.filter(function(item)
    return item.word.toLowerCase().startsWith( 'gk );
)

见https://jsfiddle.net/maheshwaghmare/cfx3p40v/4/

【讨论】:

【参考方案9】:

由于您已经在使用 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

【讨论】:

使用=== 而不是== 会更安全,以避免JavaScript 的== 运算符出现奇怪的问题。 @VickyChijwani:比较字符串和字符串时有什么问题吗? 好吧,如果你绝对确定e.idid 都是字符串,我想使用== 是可以的。但是如果你不确定,你可能会遇到问题(因为'' == 0true'' === 0false)。更不用说=== 似乎更快(***.com/questions/359494/…)。 基本上我总是使用===,因为它完全==在其他编程语言中一样工作。我认为 == 在 JavaScript 中不存在。 @de。这里的许多答案在查找唯一值时提供了预期的行为;您基本上可以通过它们提前返回或中断循环(或指示较低级别的构造停止迭代)这一事实来识别它们。有关典型示例,请参阅 JaredPar 的答案,以及 Aaronius 对该答案的评论以获得相同的见解。通常,人们以这种方式区分“过滤”和“查找”功能,但术语不同。虽然效率更高,但这仍然是线性搜索,因此如果您想使用哈希表,请参阅 Aaron Digulla 的答案(注意 impl. details)。【参考方案10】:

我查找数组索引的方法:

index = myArray.map((i) => i.id).indexOf(value_of_id);
item = myArray[index];

【讨论】:

【参考方案11】:

更通用、更简短

function findFromArray(array,key,value) 
        return array.filter(function (element) 
            return element[key] == value;
        ).shift();

在您的情况下,例如。 var element = findFromArray(myArray,'id',45) 这将为您提供整个元素。

【讨论】:

【参考方案12】:

使用Array.prototype.filter()函数。

演示:https://jsfiddle.net/sumitridhal/r0cz0w5o/4/

JSON

var jsonObj =[
 
  "name": "Me",
  "info": 
   "age": "15",
   "favColor": "Green",
   "pets": true
  
 ,
 
  "name": "Alex",
  "info": 
   "age": "16",
   "favColor": "orange",
   "pets": false
  
 ,

  "name": "Kyle",
  "info": 
   "age": "15",
   "favColor": "Blue",
   "pets": false
  
 
];

过滤器

var getPerson = function(name)
    return jsonObj.filter(function(obj) 
      return obj.name === name;
    );

【讨论】:

如何在嵌套对象中搜索? like pets= false 应该返回两个对象。 在嵌套循环中对obj.info 使用.filter 方法。 var getPerson = function(name) return jsonObj.filter(function(obj) return obj.info.filter(function(info) return pets === false; ); ); 你也可以使用 es6 风格... const filterData = jsonObj.filter(obj => obj.name === 'Alex')【参考方案13】:

以下是我在纯 JavaScript 中的处理方式,以我能想到的最简单的方式在 ECMAScript 3 或更高版本中工作。一旦找到匹配项,它就会返回。

var getKeyValueById = function(array, key, id) 
    var testArray = array.slice(), test;
    while(test = testArray.pop()) 
        if (test.id === id) 
            return test[key];
        
    
    // return undefined if no matching id is found in array
    return;


var myArray = ['id':'73', 'foo':'bar', 'id':'45', 'foo':'bar']
var result = getKeyValueById(myArray, 'foo', '45');

// result is 'bar', obtained from object with id of '45'

【讨论】:

【参考方案14】:

虽然这里有许多正确答案,但其中许多都没有解决这样一个事实,即如果多次执行此操作会导致不必要的昂贵操作。在极端情况下,这可能会导致真正的性能问题。

在现实世界中,如果您正在处理大量项目并且性能是一个问题,那么最初构建查找会更快:

var items = ['id':'73','foo':'bar','id':'45','foo':'bar'];

var lookup = items.reduce((o,i)=>o[i.id]=o,);

然后您可以像这样在固定时间获取项目:

var bar = o[id];

您也可以考虑使用 Map 而不是对象作为查找:https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map

【讨论】:

【参考方案15】:

您甚至可以在纯 JavaScript 中通过使用内置的数组“过滤器”函数来做到这一点:

Array.prototype.filterObjects = function(key, value) 
    return this.filter(function(x)  return x[key] === value; )

所以现在只需传递“id”代替key 和“45”代替value,您将获得匹配 id 45 的完整对象。那就是,

myArr.filterObjects("id", "45");

【讨论】:

不要修改不属于你的对象。【参考方案16】:

我认为最简单的方法如下,但它不适用于 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:是的,这些 ES5 阵列工具肯定会对性能产生影响。为每个元素执行一个单独的函数,因此与直接的 for 循环相比,它不会非常快。 所以你是说它会更慢?此外,据我所知,它始终会扫描整个数组,而 for 循环将在第一次匹配时终止。 如果您需要支持 IE8,只需将其放入:***.com/questions/7153470/… 如果id没有元素,这段代码会抛出错误【参考方案17】:

基于公认的答案:

jQuery:

var foo = $.grep(myArray, function(e) return e.id === foo_id)
myArray.pop(foo)

或 CoffeeScript:

foo = $.grep myArray, (e) -> e.id == foo_id
myArray.pop foo

【讨论】:

【参考方案18】:

此解决方案也可能有帮助:

Array.prototype.grep = function (key, value) 
    var that = this, ret = [];
    this.forEach(function (elem, index) 
        if (elem[key] === value) 
            ret.push(that[index]);
        
    );
    return ret.length < 2 ? ret[0] : ret;
;
var bar = myArray.grep("id","45");

我就像$.grep 一样,如果找到一个对象,function 将返回该对象,而不是一个数组。

【讨论】:

不要修改不属于你的对象。 @Gothdo 我同意。如果有人不知道function will return the object, rather than an array 可能会出错,但我认为这取决于用户。【参考方案19】:

只要浏览器支持ECMA-262,第 5 版(2009 年 12 月),这应该可以工作,几乎是单行:

var bFound = myArray.some(function (obj) 
    return obj.id === 45;
);

【讨论】:

差不多。 bFound 只是一个布尔值,即 true 如果元素满足所需条件。【参考方案20】:

用途:

var retObj =;
$.each(ArrayOfObjects, function (index, obj) 

        if (obj.id === '5')  // id.toString() if it is int

            retObj = obj;
            return false;
        
    );
return retObj;

它应该通过 id 返回一个对象。

【讨论】:

你可以使用 return obj.id === 5 来缩短你的代码?对象:假;我经常使用 $.each 来遍历数组。 @marcel:那行不通。因为返回 false 将结束循环,所以它只会找到对象,如果它是数组中的第一项。【参考方案21】:

您可以从http://sugarjs.com/ 试用 Sugarjs。

它在数组上有一个非常好的方法,.find。所以你可以找到这样的元素:

array.find( id: 75 );

您也可以将具有更多属性的对象传递给它以添加另一个“where-clause”。

注意 Sugarjs 扩展了原生对象,有些人认为这很邪恶......

【讨论】:

好吧,它邪恶的,因为新的 EcmaScript 版本可能会引入同名的新方法。你猜怎么着,这是exactly what happened with find。我的建议是,如果您想扩展原生原型,请始终使用更具体的名称,将最简单的名称留给未来的标准开发。 这条评论已经有将近 2 年的历史了,今天我还是宁愿使用 lodash。但是,如果您愿意,可以在 sugarjs 网站上阅读有关此主题的信息。他们对您的意见持良好态度:sugarjs.com/native 该操作确实特别要求提供 javascript 或 jquery 解决方案【参考方案22】:

您可以使用map() 函数轻松获得:

myArray = ['id':'73','foo':'bar','id':'45','foo':'bar'];

var found = $.map(myArray, function(val) 
    return val.id == 45 ? val.foo : null;
);

//found[0] == "bar";

工作示例:http://jsfiddle.net/hunter/Pxaua/

【讨论】:

我忘了 jQuery 的 map 会自动删除 null 元素。这听起来对我和map 的常见概念具有误导性,因为结果与原始集合的长度不同。【参考方案23】:

遍历数组中的任何项目。对于您访问的每个项目,请检查该项目的 ID。如果匹配,则返回。

如果你只想要codez:

function getId(array, id) 
    for (var i = 0, len = array.length; i < len; i++) 
        if (array[i].id === id) 
            return array[i];
        
    
    return null; // Nothing found

使用 ECMAScript 5 的 Array 方法也是如此:

function getId(array, id) 
    var obj = array.filter(function (val) 
        return val.id === id;
    );

    // Filter returns an array, and we just want the matching item.
    return obj[0];

【讨论】:

【参考方案24】:

使用原生Array.reduce

var array = [ 'id':'73' ,'foo':'bar' , 'id':'45' ,'foo':'bar' , ];
var id = 73;
var found = array.reduce(function(a, b)
    return (a.id==id && a) || (b.id == id && b)
);

如果找到则返回对象元素,否则返回false

【讨论】:

请注意,IE8及以下不支持Array.reduce。【参考方案25】:
myArray.filter(function(a) return a.id == some_id_you_want )[0]

【讨论】:

旧版浏览器的 Ployfill:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… @Danilo 如何在嵌套对象中搜索? ***.com/questions/44550439/…【参考方案26】:

将“axesOptions”视为对象数组,对象格式为 :field_type => 2, :fields => [1,3,4]

function getFieldOptions(axesOptions,choice)
  var fields=[]
  axesOptions.each(function(item)
    if(item.field_type == choice)
        fields= hashToArray(item.fields)
  );
  return fields;

【讨论】:

【参考方案27】:

最短,

var theAnswerObj = _.findWhere(array, id : 42);

【讨论】:

这需要使用下划线库,OP 要求提供纯 JavaScript 或 jQuery 解决方案 一旦你包含下划线,这不是一个简短的答案!【参考方案28】:

我真的很喜欢 Aaron Digulla 提供的答案,但需要保留我的对象数组,以便以后可以遍历它。所以我修改为

	var indexer = ;
	for (var i = 0; i < array.length; i++) 
	    indexer[array[i].id] = parseInt(i);
	
	
	//Then you can access object properties in your array using 
	array[indexer[id]].property

【讨论】:

使用与最快的解决方案相同的解决方案来查找数组中的项目。但是 parseInt 在这里是多余的。【参考方案29】:

从aggaton's answer 开始,这是一个实际返回所需元素的函数(或null,如果没有找到),给定array 和一个callback 函数,它返回一个“正确”的真值元素:

function findElement(array, callback) 
    var elem;
    return array.some(function(e) 
        if (callback(e)) 
            elem = e;
            return true;
        
    ) ? elem : null;
);

请记住,这不适用于 IE8-,因为它不支持 some。可以提供一个 polyfill,或者总是有经典的 for 循环:

function findElement(array, callback) 
    for (var i = 0; i < array.length; i++)
        if (callback(array[i])) return array[i];
    return null;
);

它实际上更快更紧凑。但如果你不想重新发明***,我建议使用像 underscore 或 lodash 这样的实用程序库。

【讨论】:

【参考方案30】:

您可以使用过滤器,

  function getById(id, myArray) 
    return myArray.filter(function(obj) 
      if(obj.id == id) 
        return obj 
      
    )[0]
  

get_my_obj = getById(73, myArray);

【讨论】:

@TobiasBeuving - 使用 Array.find() 的也是普通的 JS,应该在第一次查找时停止,这样效率会更高。

以上是关于在 JavaScript 对象数组中按 id 查找对象的主要内容,如果未能解决你的问题,请参考以下文章

如何在javascript中按属性查找数组中的对象?

如何在javascript中按属性查找数组中的对象?

猫鼬查询:在数组中按 id 查找对象

如何在reactjs / javascript中按值对对象数组进行分组

如何使用javascript在嵌套对象数组中按属性分配值

在深层嵌套对象中按特定键查找所有值