ChatGPT解决这个技术问题 Extra ChatGPT

如何确定 Javascript 数组是否包含具有等于给定值的属性的对象?

我有一个像

vendors = [{
    Name: 'Magenic',
    ID: 'ABC'
  },
  {
    Name: 'Microsoft',
    ID: 'DEF'
  } // and so on... 
];

如何检查此数组以查看“Magenic”是否存在?我不想循环,除非我必须这样做。我正在处理可能有几千条记录。

@CAFxX 解决方案更好,如果您更新选定的解决方案,那就太棒了。
同意,之前没看到!
您现在可以通过使用箭头函数进一步简化此操作。所有现代浏览器都支持这一点并且看起来更好。
@eMarine:OP 问题特别提到性能是主要问题。因此,使用 filtersome 虽然很漂亮,但不如使用显式循环好。 (由于必须为数组中的每个元素执行 lambda,它们会导致性能下降。)
420 不能投票,但这个问题显示了研究工作,并且有用且清晰

C
CAFxX

无需重新发明 wheel 循环,至少无需明确(使用 arrow functionsmodern browsers only):

if (vendors.filter(e => e.Name === 'Magenic').length > 0) {
  /* vendors contains the element we're looking for */
}

或者,更好的是,因为它允许浏览器在找到一个匹配的元素后立即停止,所以它会更快:

if (vendors.some(e => e.Name === 'Magenic')) {
  /* vendors contains the element we're looking for */
}

编辑:如果您需要与糟糕的浏览器兼容,那么您最好的选择是:

if (vendors.filter(function(e) { return e.Name === 'Magenic'; }).length > 0) {
  /* vendors contains the element we're looking for */
}

@CAFxX 找到后如何获取索引?这甚至是一种可能性还是循环会更好地获取索引?
@Echtniet 如果您需要索引,那么vendors.findIndex 将为您提供第一个匹配元素的索引。相反,如果您需要该值,则 vendor.find 将给出第一个匹配元素,或者 vendor.filter 将给出所有匹配元素。您可能需要参考 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
为什么some 更好
@7hibault 因为一旦找到带有 name === "Magenic" 的对象,some 就会短路。使用 filter,它将检查每个项目直到数组末尾并创建一个符合条件的新数组项目,然后检查 length
很多关于 .some 的评论。现在是 2019 年,使用 .some 并使用 Polyfills 来支持糟糕的浏览器并继续你的生活...... polyfill.io/v3/url-builder。我唯一能看到的是,如果你不能支持箭头函数,那么它就像我提到的 Polyfill 一样简单:arr.some(function(i) { return i.Name === "Magenic" })
A
Alex Turpin

2018 年编辑:这个答案来自 2011 年,当时浏览器还没有广泛支持数组过滤方法和箭头函数。看看CAFxX's answer

没有“神奇”的方法可以在没有循环的情况下检查数组中的某些内容。即使您使用某些函数,该函数本身也会使用循环。您可以做的是在您找到所需内容后立即跳出循环,以最大限度地减少计算时间。

var found = false;
for(var i = 0; i < vendors.length; i++) {
    if (vendors[i].Name == 'Magenic') {
        found = true;
        break;
    }
}

没问题。请记住,Keith's solution 也非常可行,可以避免循环。
如果您只需要知道“某物”是否在其中,则不需要标志,您可以使用数组大小检查扫描索引的值。为此,当然需要在 for 语句之前声明索引 var。
这些选项现在似乎有效:vendors.forEach、vendors.filter、vendors.reduce
JSON.stringify(vendors).indexOf('Magenic') 呢!== -1
@LastBreath 如果 'Magenic' 位于对象中的其他位置,很容易导致误报
b
boxtrain

不需要循环。想到的三种方法:

Array.prototype.some()

这是您问题的最准确答案,即“检查是否存在”,暗示布尔结果。如果有任何 'Magenic' 对象,这将是 true,否则为 false:

let hasMagenicVendor = vendors.some( vendor => vendor['Name'] === 'Magenic' )

Array.prototype.filter()

这将返回一个包含所有 'Magenic' 对象的数组,即使只有一个(将返回一个单元素数组):

let magenicVendors = vendors.filter( vendor => vendor['Name'] === 'Magenic' )

如果您尝试将其强制为布尔值,它将不起作用,因为空数组(没有“Magenic”对象)仍然是真实的。因此,只需在条件中使用 magenicVendors.length

Array.prototype.find()

这将返回第一个 'Magenic' 对象(如果没有,则返回 undefined):

let magenicVendor = vendors.find( vendor => vendor['Name'] === 'Magenic' );

这将强制转换为布尔值 OK(任何对象都是真实的,undefined 是虚假的)。

注意:我使用 vendor["Name"] 而不是 vendor.Name 因为属性名称的奇怪大小写。

注意 2:在检查名称时,没有理由使用松散相等 (==) 而不是严格相等 (===)。


需要指出的是,在底层,这些都是循环的。这些在计算上也都比简单的循环和执行操作要慢。
不妨在这里分享这份爱:stackoverflow.com/questions/21748670/… 这样更多像我这样的人不会导航到那个旧页面并做出假设。
T
TeaCoder

接受的答案仍然有效,但现在我们有一个 ECMAScript 6 本机方法 [Array.find][1][Array.some][2] 来实现相同的效果。

数组.some

如果您只想确定一个元素是否存在,即您需要一个true/false 确定,请使用 some

引用 MDN:

some() 方法测试数组中的至少一个元素是否通过了提供的函数实现的测试。如果在数组中找到所提供函数为其返回 true 的元素,则返回 true;否则返回false。它不会修改数组。

Array.find

如果要从数组中获取匹配的对象,请使用 find,否则返回 undefined

引用 MDN:

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

var arr = [];
var item1 = {
    id: 21,
    label: 'Banana',
};
var item2 = {
    id: 22,
    label: 'Apple',
};
arr.push(item1, item2);

/* note : data is the actual object that matched search criteria 
  or undefined if nothing matched */

var data = arr.find(function(ele) {
    return ele.id === 21;
});

if (data) {
    console.log('found');
    console.log(data); // This is entire object i.e. `item` not boolean
}


/* note : doesExist is a boolean thats true or false depending on of whether the data was found or not */
var doesExist = arr.some(function(ele) {
    return ele.id === 21;
});


请参阅我的 jsfiddle link IE 有一个 polyfill provided by mozilla


如果你只做 return ele.id == '2' 可能会更短,但 +1 是一个好的 ES6 解决方案。
很高兴有新的答案:) 只是想知道性能是否比上面的答案更好......
我认为重要的是要指出“数据”的返回值(当 ele.id 与 id 匹配时,例如“21”)将是数组项本身(在这种情况下,是整个项对象)。如果期望数据变量结果是“真”或“假”而不是假值,那么您会非常失望。
谢谢!我的任务有点不同。获取数组中对象的索引=> push if <0 || splice(index, 1) 这是我稍微更新的代码:const index = this.selected.indexOf(this.selected.find(s => s.id == passedObj.id))
此代码仅适用于 return ele.id === 21;;它是一个数字,而不是一个字符串。
M
Mirza Leka

这是我的做法

const found = vendors.some(item => item.Name === 'Magenic');

array.some() 方法检查数组中是否至少有一个值与条件匹配并返回布尔值。从这里开始,您可以使用:

if (found) {
// do something
} else {
// do something else
}

K
Keith.Abramo

除非你想像这样重组它:

vendors = {
    Magenic: {
      Name: 'Magenic',
      ID: 'ABC'
     },
    Microsoft: {
      Name: 'Microsoft',
      ID: 'DEF'
    } and so on... 
};

你可以做的if(vendors.Magnetic)

你将不得不循环


如果他仍然想维护对象结构以在其他地方使用它
你会怎么做?
L
Lucas Bento

根据 ECMAScript 6 规范,您可以使用 findIndex

const magenicIndex = vendors.findIndex(vendor => vendor.Name === 'Magenic');

magenicIndex 将保存 0(它是数组中的索引)或 -1(如果未找到)。


只是为了让人们意识到如果将其用作条件,0 仍然会匹配为错误结果。出于这个原因,我认为 find() 更好,因为您可以获得更合乎逻辑的真实评估。
不仅是@dhj 提到的,而且它可能会在以后的索引中找到(1、2 等)。因此,您需要检查索引是否至少为 0,因此大多数直接产生布尔可用值的解决方案会更加优雅。
A
Akinjiola Toni

可能为时已晚,但 javascript 数组有两个方法 someevery 方法返回一个布尔值,可以帮助您实现这一点。

我认为 some 最适合您打算实现的目标。

vendors.some( vendor => vendor['Name'] !== 'Magenic' )

有些验证数组中的任何对象都满足给定条件。

vendors.every( vendor => vendor['Name'] !== 'Magenic' )

Every 验证数组中的所有对象都满足给定条件。


@ThanwaCh。 - 它应该返回错误!在您的情况下,您需要使用 array.some 方法!
J
Jay Chakra

正如 OP 已询问密钥是否存在的问题。

使用 ES6 reduce 函数返回布尔值的更优雅的解决方案是

const magenicVendorExists =  vendors.reduce((accumulator, vendor) => (accumulator||vendor.Name === "Magenic"), false);

注意:reduce的初始参数是一个false,如果数组有key则返回true。

希望它有助于更好和更清洁的代码实现


从什么时候 !![] 等于 false?
不错的收获。使用 reduce 更新答案:)
这是错误的。 reduce 的第一个参数是累加器,而不是 vendor 对象。这会在每个循环中检查 false.Name === "Magenic" 并返回 false
@adiga:已更正。
另请检查 Mirza Leka 的解决方案。一个更优雅的解决方案。
j
jAndy

你不能不真正地观察对象。

你可能应该稍微改变你的结构,比如

vendors = {
    Magenic:   'ABC',
    Microsoft: 'DEF'
};

然后你可以像查找哈希一样使用它。

vendors['Microsoft']; // 'DEF'
vendors['Apple']; // undefined

W
Willem van der Veen

测试数组元素:

JS 提供数组函数,让您相对容易地实现这一点。它们是:

Array.prototype.filter:接受一个回调函数,它是一个测试,然后用is回调迭代数组,并根据这个回调过滤。返回一个新的过滤数组。 Array.prototype.some:接受一个作为测试的回调函数,然后使用回调函数迭代数组,如果任何元素通过测试,则返回布尔值true。否则返回 false

具体细节最好通过一个例子来解释:

例子:

vendor = [ { Name: 'Magenic', ID: 'ABC' }, { Name: 'Microsoft', ID: 'DEF' } //依此类推数组... ]; // filter 返回一个新数组,我们立即检查这个新创建的数组的长度 // 是否大于零 if (vendors.filter(company => company.Name === 'Magenic').length ) { console. log('我包含 Magenic'); } // some 会是一个比过滤更好的选择,因为它直接返回一个布尔值 if (vendors.some(company => company.Name === 'Magenic')) { console.log('I also contains Magenic'); }

浏览器支持:

这 2 个函数是 ES6 函数,并非所有浏览器都支持它们。为了克服这个问题,您可以使用 polyfill。这是 Array.prototype.some 的 polyfill(来自 MDN):

if (!Array.prototype.some) { Array.prototype.some = function(fun, thisArg) { 'use strict'; if (this == null) { throw new TypeError('Array.prototype.some call on null or undefined'); } if (typeof fun !== 'function') { throw new TypeError(); } var t = 对象(这个); var len = t.length >>> 0; for (var i = 0; i < len; i++) { if (i in t && fun.call(thisArg, t[i], i, t)) { return true; } } 返回假; }; }


H
Hisham Kuntawala
const check = vendors.find((item)=>item.Name==='Magenic')

console.log(check)

试试这个代码。

如果项目或元素存在,则输出将显示该元素。如果它不存在,则输出将是“未定义”。


J
JBallin

2021 年解决方案*

Lodash .some (docs) 是一个干净的解决方案,如果您使用 _matchesProperty (docs) 速记:

_.some(VENDORS, ['Name', 'Magenic'])

解释

这将遍历 VENDORS 数组以查找具有 Name 键的元素对象,该键具有字符串 'Magenic' 的值。一旦找到这个元素,它就会返回 true 并停止迭代。如果遍历整个 Array 后没有找到该元素,则返回 false

代码片段

const VENDORS = [{ 名称:'Magenic',ID:'ABC'},{ 名称:'Microsoft',ID:'DEF'}]; console.log(_.some(VENDORS, ['Name', 'Magenic'])); // true

* 请注意,这使用流行的 lodash 库来实现最简单/最短的解决方案。对于那些感兴趣的人,我提供了它作为现有 vanilla JS 解决方案的替代方案。


T
Tomalak

你必须循环,没有办法绕过它。

function seekVendor(vendors, name) {
  for (var i=0, l=vendors.length; i<l; i++) {
    if (typeof vendors[i] == "object" && vendors[i].Name === name) {
      return vendors[i];
    }
  }
}

当然,您可以使用像 linq.js 这样的库来使这更令人愉悦:

Enumerable.From(vendors).Where("$.Name == 'Magenic'").First();

(有关演示,请参见 jsFiddle

我怀疑 linq.js 会比直接循环更快,但当事情变得更复杂时,它肯定会更灵活。


N
Nerdy Sid

如果我错了,请纠正我.. 我可以使用这样的 forEach 方法,

var found=false;
vendors.forEach(function(item){
   if(item.name === "name"){
       found=true;

   }
});

现在我已经习惯了,因为它简单且不言自明。谢谢你。


注意:这里没有使用return
r
rotimi-best

我解决这个问题的方法是使用 ES6 并创建一个为我们进行检查的函数。此函数的好处是它可以在整个项目中重复使用,以检查给定 keyvalue 的任何对象数组进行检查。

说够了,让我们看看代码

大批

const ceos = [
  {
    name: "Jeff Bezos",
    company: "Amazon"
  }, 
  {
    name: "Mark Zuckerberg",
    company: "Facebook"
  }, 
  {
    name: "Tim Cook",
    company: "Apple"
  }
];

功能

const arrayIncludesInObj = (arr, key, valueToCheck) => {
  return arr.some(value => value[key] === valueToCheck);
}

通话/使用

const found = arrayIncludesInObj(ceos, "name", "Tim Cook"); // true

const found = arrayIncludesInObj(ceos, "name", "Tim Bezos"); // false

@SebastianSimon 我已经更新了我的代码。当我写这个答案时,我想要更少的经验。
y
yurin

函数 mapfilterfind 和类似函数比简单循环慢。对我来说,它们的可读性也比简单循环低,而且更难调试。使用它们看起来像是一种非理性的仪式。

最好有这样的东西:

 arrayHelper = {
     arrayContainsObject: function (array, object, key){
         for (let i = 0; i < array.length; i++){
            if (object[key] === array[i][key]){
                 return true;
            }
         }
         return false;
     }
     
   };

并在给定的 OP 示例中像这样使用它:

    vendors = [{
    Name: 'Magenic',
    ID: 'ABC'
     },
     {
    Name: 'Microsoft',
    ID: 'DEF'
     } 
  ];

let abcObject = {ID: 'ABC', Name: 'Magenic'};

let isContainObject = arrayHelper.arrayContainsObject(vendors, abcObject, 'ID');

map、filter、find 更易读,代码也更小,写起来更快
为什么它们比简单循环慢得多?据我所知,它们的时间复杂度与它们的简单循环等价物相同。例如,您的两个代码在我看来都像 O(n)。
上述方法 arrayContainsObject 应该是您编写一次就忘记的库方法。如果您愿意,您实际上可以使用数组函数编写它。从可读性的角度来看,没有什么比 arrayHelper.arrayContainsObject 更好的了。
@Michael Fulton leanylabs.com/blog/js-forEach-map-reduce-vs-for-for_of这篇文章的基准状态循环快了 3 倍。
@JulioSpinelli。同意,为什么不呢?但是我们应该将我们的方法重命名为 findIndexOfObject。所以,最好同时拥有它们。
Z
Zach Jensz

迄今为止最简单的方法:

if (vendors.findIndex(item => item.Name == "Magenic") == -1) {
  //not found item
} else {
  //found item 
}

E
Eitan

如果您使用的是 jquery,您可以利用 grep 创建包含所有匹配对象的数组:

var results = $.grep(vendors, function (e) {
    return e.Name == "Magenic";
});

然后使用结果数组:

for (var i=0, l=results.length; i<l; i++) {
    console.log(results[i].ID);
}

A
Abhay Shiro

你可以使用 lodash。如果 lodash 库对您的应用程序来说太重,请考虑分块未使用的不必要函数。

let newArray = filter(_this.props.ArrayOne, function(item) {
                    return find(_this.props.ArrayTwo, {"speciesId": item.speciesId});
                });

这只是执行此操作的一种方法。另一个可以是:

var newArray=  [];
     _.filter(ArrayOne, function(item) {
                        return AllSpecies.forEach(function(cItem){
                            if (cItem.speciesId == item.speciesId){
                            newArray.push(item);
                          }
                        }) 
                    });

console.log(arr);

上面的示例也可以在不使用任何库的情况下重写,例如:

var newArray=  [];
ArrayOne.filter(function(item) {
                return ArrayTwo.forEach(function(cItem){
                    if (cItem.speciesId == item.speciesId){
                    newArray.push(item);
                  }
                }) 
            });
console.log(arr);

希望我的回答有帮助。


j
jesusverma

这里的许多答案都很好而且很容易。但是,如果您的对象数组具有一组固定的值,那么您可以使用以下技巧:

映射对象中的所有名称。

vendors = [
    {
      Name: 'Magenic',
      ID: 'ABC'
     },
    {
      Name: 'Microsoft',
      ID: 'DEF'
    }
];

var dirtyObj = {}
for(var count=0;count<vendors.length;count++){
   dirtyObj[vendors[count].Name] = true //or assign which gives you true.
}

现在这个dirtyObj 可以在没有任何循环的情况下一次又一次地使用。

if(dirtyObj[vendor.Name]){
  console.log("Hey! I am available.");
}

S
Spangle

为了将一个对象与另一个对象进行比较,我结合了 for in 循环(用于循环对象)和 some()。您不必担心数组超出范围等,这样可以节省一些代码。可以找到关于 .some 的文档here

var productList = [{id: 'text3'}, {id: 'text2'}, {id: 'text4', product: 'Shampoo'}]; // Example of selected products
var theDatabaseList = [{id: 'text1'}, {id: 'text2'},{id: 'text3'},{id:'text4', product: 'shampoo'}];    
var  objectsFound = [];

for(let objectNumber in productList){
    var currentId = productList[objectNumber].id;   
    if (theDatabaseList.some(obj => obj.id === currentId)) {
        // Do what you need to do with the matching value here
        objectsFound.push(currentId);
    }
}
console.log(objectsFound);

我将一个对象与另一个对象进行比较的另一种方法是使用带有 Object.keys().length 的嵌套 for 循环来获取数组中对象的数量。下面的代码:

var productList = [{id: 'text3'}, {id: 'text2'}, {id: 'text4', product: 'Shampoo'}]; // Example of selected products
var theDatabaseList = [{id: 'text1'}, {id: 'text2'},{id: 'text3'},{id:'text4', product: 'shampoo'}];    
var objectsFound = [];

for(var i = 0; i < Object.keys(productList).length; i++){
        for(var j = 0; j < Object.keys(theDatabaseList).length; j++){
        if(productList[i].id === theDatabaseList[j].id){
            objectsFound.push(productList[i].id);
        }       
    }
}
console.log(objectsFound);

要回答您的确切问题,如果只是在对象中搜索一个值,您可以使用单个 for in 循环。

var vendors = [
    {
      Name: 'Magenic',
      ID: 'ABC'
     },
    {
      Name: 'Microsoft',
      ID: 'DEF'
    } 
];

for(var ojectNumbers in vendors){
    if(vendors[ojectNumbers].Name === 'Magenic'){
        console.log('object contains Magenic');
    }
}

B
BrunoWest

或者,您可以这样做:

const find = (key, needle) => return !!~vendors.findIndex(v => (v[key] === needle));

你最好说他为什么能那样做
b
behzad abbasi

var without2 = (arr, args) => arr.filter(v => v.id !== args.id); 示例:

without2([{id:1},{id:1},{id:2}],{id:2})

结果:没有2([{id:1},{id:1},{id:2}],{id:2})


我想你的意思是说 Result: [{id:1},{id:1}]
j
jenish

你可以试试这个它对我的工作。

const _ = require('lodash');

var arr = [
  {
    name: 'Jack',
    id: 1
  },
  {
    name: 'Gabriel',
    id: 2
  },
  {
    name: 'John',
    id: 3
  }
]

function findValue(arr,value) {
  return _.filter(arr, function (object) {
    return object['name'].toLowerCase().indexOf(value.toLowerCase()) >= 0;
  });
}

console.log(findValue(arr,'jack'))
//[ { name: 'Jack', id: 1 } ]

好吧,这是一个非常古老的问题,我认为它的更新已经有了当今最好的解决方案。
不是每个人都使用 lodash 或想要这种依赖。语言如何做到最好?
u
user1665355
const a = [{one:2},{two:2},{two:4}]
const b = a.filter(val => "two" in val).length;
if (b) {
   ...
}

请进行一些描述并确保您提供的示例有效..(过滤器不会更改原始数组而是克隆它)。
那没有回答问题。当您撰写此答案时,标题是“如何确定 Javascript 数组是否包含具有等于给定值的属性的对象?”。您正在检查对象是否在其原型链上具有属性,而不是对象的属性是否具有特定值。
@Sebastian Simon “如果指定的属性在指定的对象或其原型链中,则 in 运算符返回 true。”来自developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
@user1665355 是的,正确的。这就是我所说的。
s
sangwook kim

我宁愿使用正则表达式。

如果你的代码如下,

vendors = [
    {
      Name: 'Magenic',
      ID: 'ABC'
     },
    {
      Name: 'Microsoft',
      ID: 'DEF'
    }
];

我会推荐

/"Name":"Magenic"/.test(JSON.stringify(vendors))

有些人在遇到问题时会想“我知道,我会使用正则表达式”。现在他们有两个问题。
归档这个,仅仅因为你可以做某事,并不意味着你应该做。
除了恶作剧和幽默。有几种简单的对象和数组访问以及迭代方法和表达式。为什么正则表达式会成为您的选择?问题是如何确定一个数组是否包含一个对象,该对象的属性值与 "Magenic" 匹配。正则表达式答案的误报:[ { "Not the property you’re looking for": { "Name": "Magenic" } } ][ { 'Not the property you’re looking for"Name': "Magenic" } ];潜在的假阴性(特别是如果问题要求 own 属性):[ Object.create({ Name: "Magenic" }) ]