ChatGPT解决这个技术问题 Extra ChatGPT

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

我有一个数组:

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

我无法更改数组的结构。我收到了一个 45 的 id,我想为数组中的那个对象获取 'bar'

如何在 JavaScript 中或使用 jQuery 中执行此操作?


u
user229044

使用 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})。


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

由于您已经在使用 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)。更不用说 === 似乎更快(stackoverflow.com/questions/359494/…)。
基本上我总是使用 ===,因为它与其他编程语言中的 == 一样完全工作。我认为 == 在 JavaScript 中不存在。
@de。这里的许多答案在查找唯一值时提供了预期的行为;您基本上可以通过它们提前返回或中断循环(或指示较低级别的构造停止迭代)这一事实来识别它们。有关典型示例,请参阅 JaredPar 的答案,以及 Aaronius 对该答案的评论以获得相同的见解。通常,人们以这种方式区分“过滤”和“查找”功能,但术语不同。虽然效率更高,但这仍然是线性搜索,因此如果您想使用哈希表,请参阅 Aaron Digulla 的回答(注意 impl. details)。
A
Aaron Digulla

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

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 和对象将被共享。


正是我想要的。有趣的是,当我只需要改变从 CouchDB 接收到的数据并将其转换为对我有用的格式时,我如何尝试通过每次循环来尝试过度复杂化,从列表中删除每个项目,因为我发现它需要。 +1先生!
这很聪明。我无法想象其他人是如何通过查看每次使用的整个阵列来说服的。
只要您不依赖属性的顺序:stackoverflow.com/questions/4886314/…
正在使用休息;如果您知道只有一个对象可以找到,那么在循环中是一个很好的选择/改进?
@irJvV:不,这根本没有意义。如果您需要进行多次查找,上面的代码很有用。如果您只看一次,那么创建 lookup 对象就是浪费时间。
H
Henke

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


可能是因为它看起来仍然非常实验性,并且没有多少浏览器支持它,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() 返回未定义,那么您的优化会引发错误。所以这个解决方案只能在保证匹配的情况下使用。
@HerbertPeters 如果您想确保始终可以进行空检查,使用 optional chaining: myArray.find(d => d.id === 45)?.foo 非常容易。
G
GijsjanB

Underscore.js 有一个很好的方法:

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

记录一下,Lo-Dash(它通常比 Underscore 表现得更好)有一个类似的方法。此处的文档:lodash.com/docs#find
如果您只期望一个对象,那么使用 findWhere 会更有效,因为在找到一个结果后,搜索将不再进行。
@Foreever 来自 _.find 的文档:“该函数在找到可接受的元素后立即返回,并且不会遍历整个列表。”
P
Peter Mortensen

我认为最简单的方法如下,但它不适用于 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 的支持,只需将其放入:stackoverflow.com/questions/7153470/…
如果没有具有该 id 的元素,此代码将引发错误
J
JaredPar

尝试以下

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

这不值得自己回答,但在现代浏览器中,这个解决方案可以写成:jsfiddle.net/rwaldron/j3vST
如果您要提高效率,请注意,此示例可能比使用 filter() 更快(请参阅 Rick 的示例),因为一旦找到第一个匹配项,此示例就会返回,而 filter() 即使在找到一个之后仍会继续运行整个数组匹配。这也没有为每个项目创建额外数组或调用函数的成本。
@Rick,关于该答案最有趣的事情显然是您可以将 firebug 控制台添加到 jsFiddle 的输出窗口中。这比记录并告诉其他人打开控制台查看输出要好得多。惊人的!
由于到目前为止没有人提到它,我想补充一点,AngularJS 也有一个 filter 方法。
D
Danilo Colasso
myArray.filter(function(a){ return a.id == some_id_you_want })[0]

@Danilo 我如何在嵌套对象中搜索? stackoverflow.com/questions/44550439/…
w
will Farrell

上述 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');

K
Kamil Kiełczewski

表现

今天 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


这应该是正确的答案。当谈到循环时,性能应该是一个重要的标准。
N
Nick Parsons

正如其他人指出的那样,.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); // 找不到对象时未定义


J
Jonas Wilms

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

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

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

map.get(27).foo

C
Community

使用原生 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。
P
Peter Mortensen

您可以使用 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 的常见概念具有误导性,因为结果与原始集合的长度不同。
J
Joe Lewis

您可以使用过滤器,

  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,应该在第一次查找时停止,这样会更有效率。
T
Tom

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

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

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


m
maheshwaghmare

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

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

代码:

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


P
Peter Mortensen

遍历数组中的任何项目。对于您访问的每个项目,请检查该项目的 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];
}

P
Peter Mortensen

您可以从 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 解决方案
P
Peter Mortensen

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

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

几乎。 bFound 只是一个布尔值,它是 true 如果元素满足所需条件。
D
Dan W

以下是我在纯 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'

S
Savan Kaneriya

更通用和简短

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

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


S
Samuel Liew

我们可以使用 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


P
Peter Mortensen

基于公认的答案:

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

S
Sumit Ridhal

使用 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;
    });
}

如何在嵌套对象中搜索?像 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')
k
kaizer1v

您甚至可以在纯 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");

不要修改您不拥有的对象。
q
quincyaft

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

变量索引器 = {}; for (var i = 0; i < array.length; i++) { indexer[array[i].id] = parseInt(i); } //然后你可以使用 array[indexer[id]].property 访问你的数组中的对象属性


使用与最快的解决方案相同的解决方案来查找数组中的项目。但是 parseInt 在这里是多余的。
P
Peter Mortensen

利用:

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 将结束循环,所以它只会找到对象,如果它是数组中的第一项。
P
Peter Mortensen

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

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 可能会出错,但我认为这取决于用户。
C
Community

动态缓存查找

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

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); // 缓存