如何确定JavaScript数组是否包含一个属性等于给定值的对象

1356
我有一个数组,像这样

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

如何检查这个数组是否存在"Magenic"?除非必须,我不想使用循环。我正在处理可能有几千条记录的数据。

11
@CAFxX的解决方案更好,如果您更新所选的解决方案将会很棒。 - eMarine
3
同意,之前没看到! - David Lozzi
3
@eMarine: OP的问题明确指出性能是首要考虑因素。因此,使用filtersome虽然很好看,但不如使用显式循环效果好(它们由于必须对数组中的每个元素执行lambda而导致性能下降)。 - logidelic
4
420 无法点赞,但这个问题展示了一定的研究努力,并且很有用和清晰。 - Jeannie
2
@DavidLozzi 使用.some比使用.filter更高效,因为它在找到第一个符合条件的实例后就停止搜索。 - hitautodestruct
显示剩余3条评论
28个回答

2103
没有必要重新发明轮子,至少不需要显式地这样做(只适用于箭头函数现代浏览器):
if (vendors.filter(e => e.Name === 'Magenic').length > 0) {
  /* vendors contains the element we're looking for */
}

或者,更好的是,使用some,因为它允许浏览器在找到匹配的元素后立即停止,所以速度会更快。
if (vendors.some(e => e.Name === 'Magenic')) {
  /* vendors contains the element we're looking for */
}

或者等值的(在这种情况下)找到
if (vendors.find(e => e.Name === 'Magenic')) {
  /* same result as above, but a different function return type */
}

你甚至可以通过使用findIndex来获取该元素的位置。
const i = vendors.findIndex(e => e.Name === 'Magenic');
if (i > -1) {
  /* vendors contains the element we're looking for, at index "i" */
}

如果你需要与糟糕的浏览器兼容,那么你最好的选择是:
if (vendors.filter(function(e) { return e.Name === 'Magenic'; }).length > 0) {
  /* vendors contains the element we're looking for */
}

4
你如何在找到元素后得到索引值?这个可能吗?或者使用循环更好地获得索引? - Echtniet
4
如果你需要索引,则可以使用vendors.findIndex获取第一个匹配元素的索引。如果你需要值,则可以使用vendors.find获取第一个匹配元素,或者使用vendors.filter获取所有匹配元素。你可以参考https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array。 - CAFxX
2
为什么 some 更好呢? - 7hibault
23
因为当找到一个 name === "Magenic" 的对象时,some 可以短路。而使用 filter 会检查数组中的每个项目直到结束,并创建匹配条件的新数组项,然后检查 length - adiga
8
许多关于 .some 的评论。现在是2019年,使用 .some 并使用 Polyfills 来支持差劲的浏览器,继续前进吧... polyfill.io/v3/url-builder。我唯一能看到的是,如果你无法支持箭头函数,那么就像我提到的 Polyfill 一样简单: arr.some(function(i) { return i.Name === "Magenic" }) - maxshuty
显示剩余7条评论

381

2018年修订:此答案来自2011年,在浏览器普遍支持数组过滤方法和箭头函数之前。请看一下CAFxX的答案

没有“神奇”的方法可以在不使用循环的情况下检查数组中的某些内容。即使你使用一些函数,该函数本身也会使用循环。你所能做的就是在找到所需内容后尽快跳出循环,以最小化计算时间。

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

5
没问题。记住,Keith 的解决方案 也非常可行,可以避免循环。 - Alex Turpin
2
如果您只需要知道“某物”是否存在,而不需要标志,则无需使用标志,您可以通过将扫描索引的值与数组大小进行比较来检查。当然,在for语句之前需要声明索引变量才能使其正常工作。 - Alex
7
现在似乎这些选项可行:vendors.forEach、vendors.filter、vendors.reduce。 - David Lozzi
4
如果对象中其他地方有'Magenic',那么很容易导致假阳性。 - Alex Turpin
1
警告:错误的答案已被标记为正确。 - arled
显示剩余4条评论

212
不需要循环。我想到了三种方法:

Array.prototype.some()

这是对你的问题最准确的答案,即“检查某物是否存在”,暗示一个布尔结果。如果存在任何'Magenic'对象,这将为真,否则为假。
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:在检查名称时,没有理由使用松散相等性(==)而不是严格相等性(===)。

11
值得指出的是,在底层实现上,这些都是循环操作。与简单的for循环和操作相比,它们的计算速度更慢。 - ThePartyTurtle
不妨在这里分享一下:https://dev59.com/mmEi5IYBdhLWcg3wCYGY,这样更多像我这样的人就不会跳转到那个旧页面并做出假设了。 - ThePartyTurtle
@ThePartyTurtle:没错!!虽然开发人员的工时通常比用户的 CPU 工时更昂贵(而且更少的代码通常更好)。 - Herbert Van-Vliet

82
接受的答案仍然有效,但现在我们有 ECMAScript 6 的原生方法 [Array.find][1] 和 [Array.some][2] 可以实现相同的效果。

Array.some

如果你只想确定一个元素是否存在,即需要一个true/false的判断,可以使用some

引用MDN的说法:

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

Array.find

使用find方法可以从数组中获取匹配的对象,否则返回undefined。
引用MDN的说法:
find()方法返回提供的数组中满足提供的测试函数的第一个元素的值。如果没有值满足测试函数,则返回undefined。
let arr = [
  {
    id: 21,
    label: 'Banana',
  },
  {
    id: 22,
    label: 'Apple',
  }
]

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

let 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 */
let doesExist = arr.some(function(ele) {
    return ele.id === 21;
});



看看我的jsfiddle链接。这里有一个由Mozilla提供的IE的polyfill链接

2
如果你只想让它更短,可以写成 return ele.id == '2',但是对于这个好的 ES6 解决方案,给一个加一。 - Lye Fish
很高兴能得到新的答案 :) 只是想知道性能是否比上面的答案更好... - Emidomenge
我认为重要的是指出,当ele.id匹配一个ID(如“21”)时,“data”的返回值将是数组项本身(在这种情况下是整个项对象)。如果期望的是data变量的结果是'true'或'false',而不是一个falsy值,那么你会非常失望。 - adamgede
谢谢!我的任务有点不同。获取数组中对象的索引 => 如果<0,则push或使用splice(index,1),这里是我稍微更新的代码:const index = this.selected.indexOf(this.selected.find(s => s.id == passedObj.id)) - Leonid Zadorozhnykh
这段代码只有在 return ele.id === 21; 的情况下才能正常工作;它是一个数字,而不是字符串。 - user5161995

52

这是我会做的方式

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

array.some() 方法检查数组中是否至少有一个值符合条件,并返回布尔值。

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

30

除非你想像这样重新组织它:

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

你可以通过 if(vendors.Magnetic) 来执行某些操作。

但是你需要使用循环语句来实现。


3
如果他仍想保持对象结构以便在其他地方使用。 - Keith.Abramo
你会怎么做? - Sizzling Code

28
JavaScript数组有两个方法,即someevery方法,它们返回一个布尔值,并且可以帮助你实现这个目标。
我认为对于你想要实现的目标来说,some方法可能是最合适的选择。
vendors.some(vendor => vendor['Name'] !== 'Magenic')

一些验证,数组中的任何对象都满足给定条件。
vendors.every(vendor => vendor['Name'] !== 'Magenic')

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

@ThanwaCh. - 它应该返回false!在你的情况下,你需要使用array.some方法! - Yuriy Anisimov

26
根据ECMAScript 6规范,您可以使用findIndexconst magenicIndex = vendors.findIndex(vendor => vendor.Name === 'Magenic'); magenicIndex将保存0(数组中的索引)或-1(如果未找到)。

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

23

正如 OP 提出的问题关键是存在与否

一个更优雅的解决方案是使用 ES6reduce 函数返回一个布尔值:

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

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


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

14

你不能在没有真正查看对象的情况下进行操作。

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

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

那么你可以像使用查找哈希表一样使用它。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接