JavaScript 中如何检查数组是否包含对象?
-
04-07-2019 - |
题
确定 JavaScript 数组是否包含对象的最简洁、最有效的方法是什么?
这是我知道的唯一方法:
function contains(a, obj) {
for (var i = 0; i < a.length; i++) {
if (a[i] === obj) {
return true;
}
}
return false;
}
有没有更好、更简洁的方法来实现这一点?
这与 Stack Overflow 问题密切相关 在 JavaScript 数组中查找项目的最佳方法? 它使用以下方法解决在数组中查找对象的问题 indexOf
.
解决方案
目前的浏览器有 Array#includes
, ,这确实 确切地 那, 得到广泛支持, ,并且有一个 填充材料 对于较旧的浏览器。
> ['joe', 'jane', 'mary'].includes('jane');
true
您还可以使用 Array#indexOf
, ,这不太直接,但对于过时的浏览器不需要 Polyfill。
jQuery 提供 $.inArray
, ,其功能等价于 Array#indexOf
.
下划线.js, ,一个 JavaScript 实用程序库,提供 _.contains(list, value)
, 别名 _.include(list, value)
, ,两者都使用 指数 如果传递 JavaScript 数组,则在内部。
其他一些框架提供了类似的方法:
- 道场工具包:
dojo.indexOf(array, value, [fromIndex, findLast])
- 原型:
array.indexOf(value)
- 穆工具:
array.indexOf(value)
- 麻糬套件:
findValue(array, value)
- 阿贾克斯女士:
array.indexOf(value)
- 分机:
Ext.Array.contains(array, value)
- 洛达什:
_.includes(array, value, [from])
(是_.contains
4.0.0 之前的版本) - ECMAScript 2016:
array.includes(value)
请注意,某些框架将其实现为函数,而其他框架则将该函数添加到数组原型中。
其他提示
更新:正如@orip在评论中提到的那样,链接的基准测试是在2008年完成的,因此结果可能与现代浏览器无关。但是,你可能还需要这个来支持非现代浏览器,因为它们可能还没有更新过。总是为自己测试。
正如其他人所说,通过数组的迭代可能是最好的方法,但它已经事实证明,减少而
循环是在JavaScript中迭代的最快方式。因此,您可能需要按如下方式重写代码:
function contains(a, obj) {
var i = a.length;
while (i--) {
if (a[i] === obj) {
return true;
}
}
return false;
}
当然,您也可以扩展Array原型:
Array.prototype.contains = function(obj) {
var i = this.length;
while (i--) {
if (this[i] === obj) {
return true;
}
}
return false;
}
现在您只需使用以下内容:
alert([1, 2, 3].contains(2)); // => true
alert([1, 2, 3].contains('2')); // => false
indexOf
也许,但它是“ECMA-262标准的JavaScript扩展;因此,它可能不存在于标准的其他实现中。“
示例:
[1, 2, 3].indexOf(1) => 0
["foo", "bar", "baz"].indexOf("bar") => 1
[1, 2, 3].indexOf(4) => -1
AFAICS Microsoft 不为此提供某种替代方案,但是如果您愿意,可以在Internet Explorer(以及其他不支持 indexOf
的浏览器)中为数组添加类似功能,作为快速Google搜索显示(例如,这一个)。
ECMAScript 7引入了 Array。 prototype.includes 代码>
。
可以像这样使用:
[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false
它还接受可选的第二个参数 fromIndex
:
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
与 indexOf
不同,后者使用严格的平等比较, includes
比较使用 SameValueZero 等式算法。这意味着您可以检测数组是否包含 NaN
:
[1, 2, NaN].includes(NaN); // true
与 indexOf
不同, includes
不会跳过缺失的索引:
new Array(5).includes(undefined); // true
目前它仍然是草案,但可以 polyfilled 使其适用于所有浏览器。
b
是值, a
是数组。它返回 true
或 false
:
function(a, b) {
return a.indexOf(b) != -1
}
顶部答案假定基本类型,但如果要查明数组是否包含具有某些特征的对象, Array.prototype.some()是一个非常优雅的解决方案:
const items = [ {a: '1'}, {a: '2'}, {a: '3'} ]
items.some(item => item.a === '3') // returns true
items.some(item => item.a === '4') // returns false
关于它的好处是,一旦找到元素就会中止迭代,从而保存不必要的迭代周期。
此外,它非常适合 if
语句,因为它返回一个布尔值:
if (items.some(item => item.a === '3')) {
// do something
}
*正如jamess在评论中指出的那样,截至今天,即2018年9月,完全支持 Array.prototype.some()
: caniuse.com支持表
这是 JavaScript 1.6兼容实现> Array.indexOf 代码>:
if (!Array.indexOf) {
Array.indexOf = [].indexOf ?
function(arr, obj, from) {
return arr.indexOf(obj, from);
} :
function(arr, obj, from) { // (for IE6)
var l = arr.length,
i = from ? parseInt((1 * from) + (from < 0 ? l : 0), 10) : 0;
i = i < 0 ? 0 : i;
for (; i < l; i++) {
if (i in arr && arr[i] === obj) {
return i;
}
}
return -1;
};
}
使用:
function isInArray(array, search)
{
return array.indexOf(search) >= 0;
}
// Usage
if(isInArray(my_array, "my_value"))
{
//...
}
扩展JavaScript Array
对象是一个非常糟糕的主意,因为您将新属性(您的自定义方法)引入 for-in
循环,这可能会破坏现有脚本。几年前, Prototype 库的作者必须重新设计他们的库实现才能删除这种事。
如果您不需要担心与页面上运行的其他JavaScript的兼容性,那就去吧,否则,我会推荐更笨拙但更安全的独立功能解决方案。
开箱即用,如果你多次拨打这个电话,使用一个关联数组一个Map来使用哈希函数进行查找会更有效率。
https://developer.mozilla.org/ EN-US /文档/网络/的JavaScript /参考/ Global_Objects /导航
一衬垫:
function contains(arr, x) {
return arr.filter(function(elem) { return elem == x }).length > 0;
}
我使用以下内容:
Array.prototype.contains = function (v) {
return this.indexOf(v) > -1;
}
var a = [ 'foo', 'bar' ];
a.contains('foo'); // true
a.contains('fox'); // false
function contains(a, obj) {
return a.some(function(element){return element == obj;})
}
Array.prototype.some()已添加符合第5版的ECMA-262标准
希望更快的双向 indexOf
/ lastIndexOf
替代
2015
新方法包括非常好,现在支持基本上为零。
很长一段时间我一直想着替换慢的indexOf / lastIndexOf函数。
已找到一种高效的方式,查看最佳答案。从那些我选择@Damir Zekic发布的 contains
函数开始,这应该是最快的。但它也指出基准是从2008年开始的,因此已经过时了。
我还喜欢而
而不是
,但由于没有特殊原因,我结束了用for循环编写函数。也可以使用 while -
。
如果在执行此操作时检查数组的两侧,迭代速度要慢得多,我很好奇。显然不是,所以这个功能比最高投票的功能快两倍左右。显然它也比原生的更快。这在现实环境中,您永远不知道您正在搜索的值是在数组的开头还是结尾。
当你知道你刚推送一个带有值的数组时,使用lastIndexOf仍然可能是最好的解决方案,但是如果你必须通过大数组并且结果可能无处不在,这可能是一个可以让事情变得更快的可靠解决方案。
双向indexOf / lastIndexOf
function bidirectionalIndexOf(a, b, c, d, e){
for(c=a.length,d=c*1; c--; ){
if(a[c]==b) return c; //or this[c]===b
if(a[e=d-1-c]==b) return e; //or a[e=d-1-c]===b
}
return -1
}
//Usage
bidirectionalIndexOf(array,'value');
性能测试
http://jsperf.com/bidirectionalindexof
作为测试,我创建了一个包含100k条目的数组。
三个问题:开头,中间和在数组的末尾。
我希望你也能发现这个有趣并测试表现。
注意:正如您所看到的,我稍微修改了 contains
函数以反映indexOf&amp; lastIndexOf输出(基本上 true
, index
, false
, -1
)。这不应该伤害它。
数组原型变体
Object.defineProperty(Array.prototype,'bidirectionalIndexOf',{value:function(b,c,d,e){
for(c=this.length,d=c*1; c--; ){
if(this[c]==b) return c; //or this[c]===b
if(this[e=d-1-c] == b) return e; //or this[e=d-1-c]===b
}
return -1
},writable:false, enumerable:false});
// Usage
array.bidirectionalIndexOf('value');
也可以轻松修改该函数以返回true或false,甚至是对象,字符串或其他任何内容。
这是 while
变体:
function bidirectionalIndexOf(a, b, c, d){
c=a.length; d=c-1;
while(c--){
if(b===a[c]) return c;
if(b===a[d-c]) return d-c;
}
return c
}
// Usage
bidirectionalIndexOf(array,'value');
这怎么可能?
我认为在数组中获取反射索引的简单计算非常简单,它比实际循环迭代快两倍。
这是一个复杂的例子,每次迭代执行三次检查,但这只能通过更长的计算才能导致代码减速。
如果您使用的是JavaScript 1.6或更高版本(Firefox 1.5或更高版本),则可以使用 Array.indexOf 。否则,我认为你最终会得到类似于原始代码的东西。
function inArray(elem,array)
{
var len = array.length;
for(var i = 0 ; i < len;i++)
{
if(array[i] == elem){return i;}
}
return -1;
}
如果找到则返回数组索引,如果未找到则返回-1
我们使用此代码段(适用于对象,数组,字符串):
/*
* @function
* @name Object.prototype.inArray
* @description Extend Object prototype within inArray function
*
* @param {mix} needle - Search-able needle
* @param {bool} searchInKey - Search needle in keys?
*
*/
Object.defineProperty(Object.prototype, 'inArray',{
value: function(needle, searchInKey){
var object = this;
if( Object.prototype.toString.call(needle) === '[object Object]' ||
Object.prototype.toString.call(needle) === '[object Array]'){
needle = JSON.stringify(needle);
}
return Object.keys(object).some(function(key){
var value = object[key];
if( Object.prototype.toString.call(value) === '[object Object]' ||
Object.prototype.toString.call(value) === '[object Array]'){
value = JSON.stringify(value);
}
if(searchInKey){
if(value === needle || key === needle){
return true;
}
}else{
if(value === needle){
return true;
}
}
});
},
writable: true,
configurable: true,
enumerable: false
});
<强>用法:强>
var a = {one: "first", two: "second", foo: {three: "third"}};
a.inArray("first"); //true
a.inArray("foo"); //false
a.inArray("foo", true); //true - search by keys
a.inArray({three: "third"}); //true
var b = ["one", "two", "three", "four", {foo: 'val'}];
b.inArray("one"); //true
b.inArray('foo'); //false
b.inArray({foo: 'val'}) //true
b.inArray("{foo: 'val'}") //false
var c = "String";
c.inArray("S"); //true
c.inArray("s"); //false
c.inArray("2", true); //true
c.inArray("20", true); //false
使用lodash的某些功能。
它简洁,准确,并且具有很好的跨平台支持。
接受的答案甚至不符合要求。
要求:建议最简洁有效的方法来确定JavaScript数组是否包含对象。
接受的答案:
$.inArray({'b': 2}, [{'a': 1}, {'b': 2}])
> -1
我的建议:
_.some([{'a': 1}, {'b': 2}], {'b': 2})
> true
注意:
$。inArray可以很好地确定标量数组中是否存在标量值......
$.inArray(2, [1,2])
> 1
...但问题明确要求确定一个对象是否包含在数组中的有效方法。
为了处理标量和对象,你可以这样做:
(_.isObject(item)) ? _.some(ary, item) : (_.indexOf(ary, item) > -1)
适用于所有现代浏览器的解决方案:
function contains(arr, obj) {
const stringifiedObj = JSON.stringify(obj); // Cache our object to not call `JSON.stringify` on every iteration
return arr.some(item => JSON.stringify(item) === stringifiedObj);
}
用法:
contains([{a: 1}, {a: 2}], {a: 1}); // true
IE6 +解决方案:
function contains(arr, obj) {
var stringifiedObj = JSON.stringify(obj)
return arr.some(function (item) {
return JSON.stringify(item) === stringifiedObj;
});
}
// .some polyfill, not needed for IE9+
if (!('some' in Array.prototype)) {
Array.prototype.some = function (tester, that /*opt*/) {
for (var i = 0, n = this.length; i < n; i++) {
if (i in this && tester.call(that, this[i], i, this)) return true;
} return false;
};
}
用法:
[{a: 1}, {a: 2}].includes({a: 1});
// false, because {a: 1} is a new object
为什么要使用 JSON.stringify
?
Array.indexOf
和 Array.includes
(以及此处的大多数答案)仅按引用而不是按值进行比较。
[{a: 1}, {a: 2}].some(item => JSON.stringify(item) === JSON.stringify({a: 1));
// true
加成
非优化的ES6单线:
<*>注意: 如果按键的顺序相同,按值比较对象会更好,所以为了安全起见,您可以先使用像这样的包对键进行排序: https://www.npmjs.com/package/sort-keys
使用perf优化更新了 contains
函数。感谢 itinance 指出。
虽然 array.indexOf(x)!= - 1
是最简洁的方法(并且非互联网资源管理器浏览器已经支持了十多年......),不是O(1),而是O(N),这是可怕的。如果您的数组不会更改,您可以将数组转换为哈希表,然后执行 table [x]!== undefined
或 === undefined
:
Array.prototype.toTable = function() {
var t = {};
this.forEach(function(x){t[x]=true});
return t;
}
演示:
var toRemove = [2,4].toTable();
[1,2,3,4,5].filter(function(x){return toRemove[x]===undefined})
(不幸的是,虽然你可以创建一个Array.prototype.contains来“冻结”一个数组并将this._cache中的哈希表存储在两行中,但是如果你以后选择编辑你的数组会产生错误的结果。没有足够的钩子让你保持这种状态,例如Python不同。)
ECMAScript 6有一个关于find的优雅提议。
find方法为每个元素执行一次回调函数 出现在数组中,直到找到一个回调返回true的地方 值。如果找到这样的元素,find会立即返回该值 那个元素。否则,find返回undefined。回调是 仅为已分配值的数组的索引调用;它 不会为已删除或从不删除的索引调用 被赋予了价值。
以下是 MDN文档关于那个。
查找功能就像这样。
function isPrime(element, index, array) {
var start = 2;
while (start <= Math.sqrt(element)) {
if (element % start++ < 1) return false;
}
return (element > 1);
}
console.log( [4, 6, 8, 12].find(isPrime) ); // Undefined, not found
console.log( [4, 5, 8, 12].find(isPrime) ); // 5
您可以在ECMAScript 5及更低版本中使用定义功能。
if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, 'find', {
enumerable: false,
configurable: true,
writable: true,
value: function(predicate) {
if (this == null) {
throw new TypeError('Array.prototype.find called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
if (i in list) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return value;
}
}
}
return undefined;
}
});
}
使用:
var myArray = ['yellow', 'orange', 'red'] ;
alert(!!~myArray.indexOf('red')); //true
要确切知道 tilde
〜
到底做了什么,请参阅此问题 当代字在表达式之前时代码做什么? 。
/**
* Array#indexOf(item[, offset = 0]) -> Number
* - item (?): A value that may or may not be in the array.
* - offset (Number): The number of initial items to skip before beginning the
* search.
*
* Returns the position of the first occurrence of `item` within the array — or
* `-1` if `item` doesn't exist in the array.
**/
function indexOf(item, i) {
i || (i = 0);
var length = this.length;
if (i < 0) i = length + i;
for (; i < length; i++)
if (this[i] === item) return i;
return -1;
}
另见此处他们如何把它搞定。
好的,你可以优化代码来获得结果!
有很多方法可以做得更干净,更好,但我只是想得到你的模式并使用 JSON.stringify
来应用它,只需在你的情况下做这样的事情:
function contains(a, obj) {
for (var i = 0; i < a.length; i++) {
if (JSON.stringify(a[i]) === JSON.stringify(obj)) {
return true;
}
}
return false;
}
使用:
Array.prototype.contains = function(x){
var retVal = -1;
// x is a primitive type
if(["string","number"].indexOf(typeof x)>=0 ){ retVal = this.indexOf(x);}
// x is a function
else if(typeof x =="function") for(var ix in this){
if((this[ix]+"")==(x+"")) retVal = ix;
}
//x is an object...
else {
var sx=JSON.stringify(x);
for(var ix in this){
if(typeof this[ix] =="object" && JSON.stringify(this[ix])==sx) retVal = ix;
}
}
//Return False if -1 else number if numeric otherwise string
return (retVal === -1)?false : ( isNaN(+retVal) ? retVal : +retVal);
}
我知道这不是最好的方法,但由于没有本地IComparable方法在对象之间进行交互,我想这就像你可以比较数组中的两个实体一样接近。此外,扩展Array对象可能不是一件明智的事情,但有时它是可以的(如果你知道它和权衡)。
可以使用设置方法“has()”:
function contains(arr, obj) {
var proxy = new Set(arr);
if (proxy.has(obj))
return true;
else
return false;
}
var arr = ['Happy', 'New', 'Year'];
console.log(contains(arr, 'Happy'));
你也可以使用这个技巧:
var arrayContains = function(object) {
return (serverList.filter(function(currentObject) {
if (currentObject === object) {
return currentObject
}
else {
return false;
}
}).length > 0) ? true : false
}
- 使用
Array.indexOf(Object)
。 - 使用ECMA 7,可以使用
Array.includes(Object)
。 -
使用ECMA 6,您可以使用
Array.find(FunctionName)
,其中FunctionName
是用户 定义函数以搜索数组中的对象。希望这有帮助!
醇>
类似的事情:通过“搜索lambda”找到第一个元素:
Array.prototype.find = function(search_lambda) {
return this[this.map(search_lambda).indexOf(true)];
};
用法:
[1,3,4,5,8,3,5].find(function(item) { return item % 2 == 0 })
=> 4
相同的coffeescript:
Array.prototype.find = (search_lambda) -> @[@map(search_lambda).indexOf(true)]