我有一个数组:
myArray = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'}, etc.]
我无法更改数组的结构。我收到了一个 45
的 id,我想为数组中的那个对象获取 'bar'
。
如何在 JavaScript 中或使用 jQuery 中执行此操作?
使用 find()
方法:
myArray.find(x => x.id === '45').foo;
从 MDN:
如果数组中的元素满足提供的测试函数,则 find() 方法返回数组中的第一个值。否则返回未定义。
如果您想找到它的 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 转译您的代码(使用 { 3})。
由于您已经在使用 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 的 ==
运算符出现奇怪的问题。
e.id
和 id
都是字符串,我想可以使用 ==
。但如果您不确定,您可能会遇到问题(因为 '' == 0
是 true
而 '' === 0
是 false
)。更不用说 ===
似乎更快(stackoverflow.com/questions/359494/…)。
===
,因为它与其他编程语言中的 ==
一样完全工作。我认为 ==
在 JavaScript 中不存在。
另一种解决方案是创建一个查找对象:
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 和对象将被共享。
lookup
对象就是浪费时间。
ECMAScript 2015 (JavaScript ES6) 为数组提供了 find() 方法:
var myArray = [ {id:1, name:"bob"}, {id:2, name:"dan"}, {id:3, name:"barb"}, ] // 获取匹配的 Array 项id "2" var item = myArray.find(item => item.id === 2); // 打印 console.log(item.name);
它可以在没有外部库的情况下工作。但如果您想要 older browser support,您可能想要包含 this polyfill。
myArray.find(d=>d.id===45).foo;
。
myArray.find(({ id }) => id === 45).foo
。但这是在 ES2015 语法和现在一样受到支持之前编写的旧答案。 @Gothdo 的 answer 目前是线程中最新的。
myArray.find(d => d.id === 45)?.foo
非常容易。
Underscore.js 有一个很好的方法:
myArray = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'},etc.]
obj = _.find(myArray, function(obj) { return obj.id == '45' })
我认为最简单的方法如下,但它不适用于 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
相比,这里有什么性能优势吗?
for
循环相比,它不会很快。
for
循环将在第一次匹配时终止。
id
的元素,此代码将引发错误
尝试以下
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;
}
myArray.filter(function(a){ return a.id == some_id_you_want })[0]
上述 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');
表现
今天 2020.06.20 我在 Chrome 81.0、Firefox 77.0 和 Safari 13.1 上对 MacOs High Sierra 进行测试,以选择解决方案。
使用预先计算的解决方案的结论
具有预先计算 (K,L) 的解决方案比其他解决方案快得多,并且不会与它们进行比较 - 可能它们使用了一些特殊的内置浏览器优化
令人惊讶的是,基于 Map (K) 的 Chrome 和 Safari 解决方案比基于 object {} (L) 的解决方案快得多
令人惊讶的是,Safari 上基于 object {} (L) 的小型数组解决方案比传统的 for (E) 慢
令人惊讶的是,基于 Map (K) 的 Firefox 小数组解决方案比传统的 (E) 慢
搜索对象始终存在时的结论
使用传统 for (E) 的解决方案对于小阵列来说最快,对于大阵列来说速度很快
使用缓存 (J) 的解决方案对于大型阵列来说是最快的 - 令人惊讶的是,对于小型阵列来说是中等速度
基于 find (A) 和 findIndex (B) 的解决方案在小型阵列上速度很快,在大型阵列上速度中等
基于 $.map (H) 的解决方案在小型阵列上最慢
基于 reduce (D) 的解决方案在大阵列上最慢
https://i.stack.imgur.com/W3dYP.png
搜索对象从不存在时的结论
基于传统 for (E) 的解决方案在小型和大型阵列上最快(Chrome 小型阵列除外,它的速度第二快)
基于 reduce (D) 的解决方案在大阵列上最慢
使用缓存 (J) 的解决方案是中等速度,但如果我们将具有空值的键也保存在缓存中,则可以加快速度(这里没有这样做,因为我们希望避免缓存中无限的内存消耗,以防许多不存在的键将被搜索)
https://i.stack.imgur.com/nvXDF.png
细节
对于解决方案
没有预先计算:ABCDEFGHIJ(J 解决方案使用“内部”缓存,它的速度取决于搜索元素重复的频率)
预先计算 KL
我进行了四次测试。在测试中,我想在 10 次循环迭代中找到 5 个对象(对象 ID 在迭代期间不会改变) - 所以我调用测试方法 50 次,但只有前 5 次具有唯一的 id 值:
小数组(10 个元素)和搜索的对象始终存在 - 您可以在此处执行
大数组(10k 个元素)和搜索的对象始终存在 - 您可以在此处执行
小数组(10 个元素)和搜索的对象从不存在 - 你可以在这里执行
大数组(10k 个元素)和搜索的对象从不存在 - 你可以在这里执行
测试代码如下
函数 A(arr, id) { return arr.find(o=> o.id==id); } 函数 B(arr, id) { 让 idx= arr.findIndex(o=> o.id==id);返回 arr[idx]; } 函数 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];返回空值; } 函数 F(arr, id) { var retObj ={}; $.each(arr, (index, obj) => { if (obj.id == id) { retObj = obj; return false; } });返回 retObj; } 函数 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]; } 函数 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); } })();函数 K(arr, id) { return mapK.get(id) } function L(arr, id) { return mapL[id]; } // ------------- // 测试 // ------------- 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]) );常量 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('整个数组',JSON.stringify(myArray)); 这个片段只展示了经过测试的代码
搜索对象始终存在的小数组的 Chrome 示例测试结果
https://i.stack.imgur.com/36oqr.png
正如其他人指出的那样,.find()
是在数组中查找一个对象时要走的路。但是,如果使用此方法无法找到您的对象,您的程序将崩溃:
const myArray = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'}]; const res = myArray.find(x => x.id === '100').foo; // 哦哦! /* 错误:“Uncaught TypeError: Cannot read property 'foo' of undefined”或在较新的 chrome 版本中: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; // 没有错误!控制台.log(res); // 找不到对象时未定义
如果你多次这样做,你可以设置一个 Map (ES6):
const map = new Map( myArray.map(el => [el.id, el]) );
然后您可以简单地进行 O(1) 查找:
map.get(27).foo
使用原生 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
您可以使用 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/
map
会自动删除 null
元素这一事实。这听起来对我和 map
的常见概念具有误导性,因为结果与原始集合的长度不同。
您可以使用过滤器,
function getById(id, myArray) {
return myArray.filter(function(obj) {
if(obj.id == id) {
return obj
}
})[0]
}
get_my_obj = getById(73, myArray);
虽然这里有许多正确答案,但其中许多都没有解决这样一个事实,即如果多次执行,这将是一项不必要的昂贵操作。在极端情况下,这可能是真正的性能问题的原因。
在现实世界中,如果您正在处理大量项目并且性能是一个问题,那么最初构建查找会更快:
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
最近,我不得不面对同样的事情,我需要从一个巨大的数组中搜索字符串。
经过一番搜索,我发现使用简单的代码很容易处理:
代码:
var items = mydata.filter(function(item){
return item.word.toLowerCase().startsWith( 'gk );
})
请参阅https://jsfiddle.net/maheshwaghmare/cfx3p40v/4/
https://i.stack.imgur.com/v3s8k.gif
遍历数组中的任何项目。对于您访问的每个项目,请检查该项目的 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];
}
您可以从 http://sugarjs.com/ 试用 Sugarjs。
它在数组上有一个非常好的方法,.find
。所以你可以找到这样的元素:
array.find( {id: 75} );
您还可以将具有更多属性的对象传递给它以添加另一个“where-clause”。
请注意,Sugarjs 扩展了原生对象,有些人认为这非常邪恶......
find
。我的建议是,如果您想扩展原生原型,请始终使用更具体的名称,将最简单的名称留给未来的标准开发。
只要浏览器支持 ECMA-262,第 5 版(2009 年 12 月),这应该可以工作,几乎单行:
var bFound = myArray.some(function (obj) {
return obj.id === 45;
});
bFound
只是一个布尔值,它是 true
如果元素满足所需条件。
以下是我在纯 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'
更通用和简短
function findFromArray(array,key,value) {
return array.filter(function (element) {
return element[key] == value;
}).shift();
}
在你的情况下,例如。 var element = findFromArray(myArray,'id',45)
这将为您提供整个元素。
我们可以使用 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
基于公认的答案:
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
使用 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;
});
}
obj.info
使用 .filter
方法。 var getPerson = function(name){ return jsonObj.filter(function(obj) { return obj.info.filter(function(info) { return pets === false; }); }); }
您甚至可以在纯 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");
我真的很喜欢 Aaron Digulla 提供的答案,但需要保留我的对象数组,以便以后可以遍历它。所以我将其修改为
变量索引器 = {}; for (var i = 0; i < array.length; i++) { indexer[array[i].id] = parseInt(i); } //然后你可以使用 array[indexer[id]].property 访问你的数组中的对象属性
利用:
var retObj ={};
$.each(ArrayOfObjects, function (index, obj) {
if (obj.id === '5') { // id.toString() if it is int
retObj = obj;
return false;
}
});
return retObj;
它应该通过 id 返回一个对象。
此解决方案也可能有帮助:
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 将返回该对象,而不是一个数组。
function will return the object, rather than an array
可能会出错,但我认为这取决于用户。
动态缓存查找
在这个解决方案中,当我们搜索某个对象时,我们将其保存在缓存中。这是“始终搜索解决方案”和“在预先计算中为每个对象创建哈希映射”之间的中间点。
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); })(); // --------- // 测试 // --------- 让 myArray = [...Array(100000)].map((x,i)=> ({' id':`${i}`, 'foo':`bar_${i}`})); // 示例用法 console.log(cachedFind(myArray,'1234').foo ); // 基准测试 let bench = (id) => { console.time ('time for '+id ); console.log ( cachedFind(myArray,id).foo ); // FIND console.timeEnd('time for '+id ); } console.log('----- 没有缓存 -----');长凳(50000);长凳(79980);长凳(99990); console.log('----- 缓存 -----');长凳(79980); // 缓存的工作台(99990); // 缓存
find
方法中添加多个条件吗?