在 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.id
和id
都是字符串,我想使用==
是可以的。但是如果你不确定,你可能会遇到问题(因为'' == 0
是true
而'' === 0
是false
)。更不用说===
似乎更快(***.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 withfind
。我的建议是,如果您想扩展原生原型,请始终使用更具体的名称,将最简单的名称留给未来的标准开发。
这条评论已经有将近 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 查找对象的主要内容,如果未能解决你的问题,请参考以下文章