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


657

我有一个像

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

如何检查此数组以查看Magenic是否存在?除非必须,否则我不想循环播放。我正在处理几千条记录。


更新

由于这是一个受欢迎的帖子,所以我想分享一些新发现。看来@CAFxX已经分享了!我应该更经常阅读这些内容。我遇到了https://benfrain.com/understanding-native-javascript-array-methods/

vendors.filter(function(vendor){ return vendor.Name === "Magenic" })

借助ECMAScript 2015,使用新的箭头功能甚至更加简单:

vendors.filter(vendor => vendor.Name === "Magenic")

请原谅似乎随机的评论,但是您的问题涉及JSON还是仅涉及JavaScript数组?
亚历克斯·特平

4
@CAFxX解决方案更好,如果您更新选定的解决方案,那就太好了。
eMarine 2015年

1
同意,之前没有看到!
David Lozzi '16

您现在可以使用箭头功能进一步简化此操作。所有现代浏览器都支持此功能,并且外观更好。
Piotr Kula

您可以使用地图功能,非常有用
Monir alhussini

Answers:


264

2018 edit:这个答案来自2011,当时浏览器没有广泛支持数组过滤方法和箭头功能。看一下CAFxX的答案

没有“魔术”的方法来检查没有循环的数组中的某些内容。即使使用某些函数,该函数本身也会使用循环。您所能做的就是在找到要查找的内容后立即打破循环,以最大程度地减少计算时间。

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

4
没问题。请记住,基思的解决方案也非常可行,可以避免循环。
亚历克斯·特平

2
如果您只需要知道“内容”是否在其中,就不需要标记,您只需检查具有数组大小的扫描索引的值即可。为此,必须在for语句之前声明索引var。
亚历克斯(Alex)

5
这些选项现在似乎可以使用:vendors.forEach,vendors.filter,vendors.reduce
David Lozzi 2016年

1
JSON.stringify(vendors).indexOf('Magenic')!== -1
最后一口气

1
@LastBreath如果'Magenic'在对象中的其他位置,很容易导致误报
Alex Turpin

947

无需重新发明 循环,至少没有明确地循环(使用箭头功能仅适用于现代浏览器):

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 */
}

4
@Rocket为什么要编辑我的答案?没有花括号的语法是完全有效的javascript
CAFxX 2011年

4
“ lambda”语法在Chrome 16(这不是一个糟糕的浏览器)中仍然不起作用。
火箭Hazmat

27
我猜这取决于您对糟糕的定义。该语法是javascript 1.8的一部分。
CAFxX 2011年

7
您在第一和第二个示例中使用的表达式闭包具有非标准,请勿使用!来自Mozilla的警告(请参阅该链接)。它们仅在Firefox中可用,现在已弃用,为了支持箭头功能将被删除。
doppelgreener 2016年

2
@ 7hibault因为some一旦name === "Magenic"找到一个对象就会短路。使用filter,它将检查每个项目,直到数组末尾并创建一个符合条件的新数组项目,然后检查length
adiga

93

无需循环。我想到了三种方法:

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

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


注意:由于属性名称的大小写很怪异,所以我使用vendor [“ Name”]而不是vendor.Name。

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


5
有必要指出的是,所有这些都是循环的。与简单地循环和执行操作相比,这些方法的计算速度也较慢。
ThePartyTurtle

也可以在这里分享喜欢的东西:stackoverflow.com/questions/21748670/…因此,更多像我这样的人不会导航到该旧页面并进行假设。
ThePartyTurtle

43

可接受的答案仍然有效,但是现在我们有了ECMAScript 6本机方法[Array.find][1]来达到相同的效果。

报价MDN:

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

var arr = []; 
var item = {
  id: '21',
  step: 'step2',
  label: 'Banana',
  price: '19$'
};

arr.push(item);
/* 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
}

参见我的jsfiddle链接。mozilla 提供了 IE的polyfill


2
如果您只是想做的话return ele.id == '2',可能会更短一些,但是对于一个好的ES6解决方案,则为+1。
碱液鱼

高兴

我认为必须指出,“数据”的返回值(当ele.id匹配一个ID,例如“ 21”时)将是数组项本身(在这种情况下,将是整个项对象)。如果期望数据变量结果是'true'或'false'而不是虚假的值,那么您将非常失望。
adamgede

谢谢!我的任务有些不同。获取数组中Object的索引=> push if <0 || splice(index, 1)这是我更新的代码:const index = this.selected.indexOf(this.selected.find(s => s.id == passedObj.id))
Leonid Zadorozhnykh

29

这是我会做的方式

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

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

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

22

除非您想要这样重组:

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

你可以做到的 if(vendors.Magnetic)

你将不得不循环


2
如果他仍然想维护对象结构以在其他地方使用它
Keith.Abramo

21

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

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

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


只是为了使人们知道,如果将0用作条件,则仍将其作为错误结果进行匹配。因此,我认为当您获得更合理的真实评估时,find()会更好。
dhj

15

由于OP询问了密钥是否存在的问题。

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

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

注意: reduce的初始参数是a false,如果数组具有键,它将返回true。

希望它有助于更​​好,更干净地执行代码


1
从什么时候开始[[]等于假?
谢尔盖(Sergey),

1
不错的收获。更新的答案使用reduce :)
Jay Chakra

1
错了 to的第一个参数reduce是累加器,而不是vendor对象。这会false.Name === "Magenic"在每个循环中检查并返回false
adiga

@adiga:已更正。
杰伊·查克拉

1
另请检查Mirza Leka的解决方案。一个更加优雅的解决方案。
杰伊·查克拉

13

您不能不真正地调查对象。

您可能应该稍微改变一下结构,例如

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

然后,您可以将其像查找哈希一样使用。

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

5

您必须循环,无法解决。

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会比直接循环更快,但是当事情变得更复杂时,它肯定会更灵活。


5

测试数组元素:

JS提供数组函数,使您可以相对轻松地实现此目的。它们是:

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

通过一个例子可以最好地解释这些细节:

例:

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

// filter returns a new array, we instantly check if the length 
// is longer than zero of this newly created array
if (vendors.filter(company => company.Name === 'Magenic').length ) {
  console.log('I contain Magenic');
}

// some would be a better option then filter since it directly returns a boolean
if (vendors.some(company => company.Name === 'Magenic')) {
  console.log('I also contain Magenic');
}

浏览器支持:

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

if (!Array.prototype.some) {
  Array.prototype.some = function(fun, thisArg) {
    'use strict';

    if (this == null) {
      throw new TypeError('Array.prototype.some called on null or undefined');
    }

    if (typeof fun !== 'function') {
      throw new TypeError();
    }

    var t = Object(this);
    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;
      }
    }

    return false;
  };
}


5

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

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

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

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

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

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


它不起作用const array1 = [{name:'Mike'},{name:'Alice'}]; console.log(array1.every(item => item.name !== 'Mike'));,应该返回true
Thanwa Ch。

对不起,朋友,我的意思是some,将更新我的答案。
Akinjiola Toni

4

如果您使用的是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);
}

3

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

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

   }
});

如今我已经习惯了,因为它简单易懂。谢谢。


1
注意:此处不使用退货
爱迪生

2

您可以为我尝试它的工作。

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

好吧,这是一个非常老的问题,我认为它的更新如今已经是最好的解决方案。
Federico Galfione '19

1

您可以使用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);

希望我的回答有帮助。


1

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

将所有名称映射到一个对象中。

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.");
}

1

为了将一个对象与另一个对象进行比较,我将for循环(用于循环遍历对象)和some()进行了组合。您不必担心数组越界越好,这样可以节省一些代码。有关.some的文档可以在这里找到

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


0

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


我想你是想说结果:[{id:1},{id:1}]
艾萨克·朴

0
const a = [{one:2},{two:2},{two:4}]
const b = a.filter(val => "two" in val).length;
if (b) {
   ...
}

3
请和一些描述并确保您提供的示例有效。(过滤器不会更改原始数组,但会对其进行克隆)。
Moshe Simantov

-1

解决这个问题的方法是使用ES6并创建一个为我们做检查的函数。此功能的好处是可以在整个项目中重用,以检查给定key和的对象数组value

够了,让我们看看代码

数组

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

功能

const arrayIncludesInObj = (arr, key, valueToCheck) => {
  let found = false;

  arr.some(value => {
    if (value[key] === valueToCheck) {
      found = true;
      return true; // this will break the loop once found
    }
  });

  return found;
}

通话/使用

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

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

-4

我宁愿使用正则表达式。

如果您的代码如下,

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

我会推荐

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

23
有些人遇到问题时会认为“我知道,我会使用正则表达式”。现在他们有两个问题。
Craicerjack

仅仅因为您可以做某事而将其归档在下面,并不意味着您应该这样做。
利亚姆'18
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.