如何检查数组是否包含JavaScript中的对象?
找出JavaScript数组是否包含对象的最简洁高效的方法是什么?
这是我知道这样做的唯一途径:
function contains(a, obj) { for (var i = 0; i < a.length; i++) { if (a[i] === obj) { return true; } } return false; }
有一个更好,更简洁的方法来实现这一目标吗?
这是非常密切相关的堆栈溢出问题在JavaScript数组中find一个项目的最佳方法? 它使用indexOf
来解决查找数组中的对象的问题。
目前的浏览器有Array#includes
,这个function正是 被广泛支持的 ,并且为老版本的浏览器提供了polyfill 。
你也可以使用Array#indexOf
,它不那么直接,但是对于过时的浏览器不需要Polyfills。
jQuery提供了$.inArray
,它在function上等同于Array#indexOf
。
underscore.js是一个JavaScript实用程序库,提供了_.contains(list, value)
,别名_.include(list, value)
,如果传递一个JavaScript数组,它们都会在内部使用indexOf 。
其他一些框架提供了类似的方法:
- Dojo Toolkit:
dojo.indexOf(array, value, [fromIndex, findLast])
- 原型:
array.indexOf(value)
- MooTools:
array.indexOf(value)
-
findValue(array, value)
:findValue(array, value)
- MS Ajax:
array.indexOf(value)
- Ext:
Ext.Array.contains(array, value)
- Lodash:
_.includes(array, value, [from])
(是_.contains
在4.0.0之前) - ECMAScript 2016:
array.includes(value)
请注意,有些框架将其作为一个函数实现,而其他框架则将该函数添加到数组原型中。
更新:正如@orip在评论中提到的,链接的基准testing是在2008年完成的,所以结果可能与现代浏览器无关。 不过,你可能需要这个来支持非现代的浏览器,并且可能从此以后就没有更新过了。 总是为自己testing。
正如其他人所说,通过数组迭代可能是最好的方法,但已经certificate , while
循环递减是在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 微软并没有提供某种替代方法 ,但是如果你愿意的话,你可以在IE浏览器(和其他不支持indexOf
浏览器)中添加类似的function,就像Google的快速search一样 (例如, 一个 )。
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
不同,它使用SameValueZero相等algorithm进行比较。 这意味着你可以检测一个数组是否包含NaN
:
[1, 2, NaN].includes(NaN); // true
与indexOf
不同的是, includes
不会跳过缺less的索引:
new Array(5).includes(undefined); // true
目前它仍然是一个草案,但可以polyfilled使其在所有浏览器上工作。
b
是数值, a
是数组。 它返回true
或false
:
function(a, b) { return a.indexOf(b) != -1 }
这里是Array.indexOf
的一个JavaScript 1.6兼容的实现:
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库的作者不得不重新devise他们的库实现来移除这种types的东西。
如果你不需要担心页面上运行的其他JavaScript的兼容性,那就去做吧,否则,我会推荐更为尴尬但更安全的独立函数解决scheme。
你可以使用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
上面这个是一旦元素被发现迭代被中止,所以不必要的迭代周期被保存。
有一件事要注意的是, some()
不是在所有的js版本中都存在:(从网站)
第5版增加了一些ECMA-262标准; 因此它可能不存在于标准的所有实现中
你可以添加它,如果它不在那里,这个polyfill(来自同一个链接)
if (!Array.prototype.some) { Array.prototype.some = function(fun /*, thisArg */) { 'use strict'; if (this === void 0 || this === null) throw new TypeError(); var t = Object(this); var len = t.length >>> 0; if (typeof fun !== 'function') throw new TypeError(); var thisArg = arguments.length >= 2 ? arguments[1] : void 0; for (var i = 0; i < len; i++) { if (i in t && fun.call(thisArg, t[i], i, t)) return true; } return false; }; }
我使用以下内容:
Array.prototype.contains = function (v) { return this.indexOf(v) > -1; } var a = [ 'foo', 'bar' ]; a.contains('foo'); // true a.contains('fox'); // false
一内胆:
function contains(arr, x) { return arr.filter(function(elem) { return elem == x }).length > 0; }
想一想,如果你多次进行这个调用,使用一个关联数组 Map来使用一个哈希函数进行查找是非常有效率的。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
function contains(a, obj) { return a.some(function(element){return element == obj;}) }
Array.prototype.some()被添加到第5版的ECMA-262标准中
希望更快的双向indexOf
/ lastIndexOf
替代
2015年
而新方法包括非常好,现在的支持基本上是零。
很长一段时间,我正在想办法来取代慢的indexOf / lastIndexOf函数。
已经find了一个高性能的方法,看最高的答案。 从那些我select@Damir Zekic发布的contains
函数应该是最快的。 但是它也说明基准是从2008年开始的,所以已经过时了。
我也喜欢,而不是因为一个特定的原因,我结束了用for循环写入函数。 这也可以做一段while --
。
我很好奇,如果我在执行数组的时候检查数组的两边,迭代速度会慢很多。 显然没有,所以这个function比顶级投票的快两倍左右。 显然这比原生的还快。 这在现实世界的环境中,你永远不知道你正在search的值是在数组的开始还是结束。
当你知道你只是用一个值推送一个数组时,使用lastIndexOf可能是最好的解决scheme,但是如果你必须穿越大数组,并且结果可能在任何地方,这可能是一个可以使事情变得更快的可靠解决scheme。
双向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');
性能testing
http://jsperf.com/bidirectionalindexof
作为testing,我创build了一个具有100K条目的数组。
三个查询:在开始,在中间和在数组的末尾。
我希望你也能find这个有趣的东西,testing一下这个performance
注意:正如你所看到的,我稍微修改了contains
函数来反映indexOf&lastIndexOf的输出结果( index
基本为true
, -1
false
)。 这不应该伤害它。
数组原型变体
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,甚至是对象,string或任何它。
这里是while
variables:
function bidirectionalIndexOf(a, b, c, d){ c=a.length; d=c-1; while(c--){ if(b===a[c]) return c; if(b===a[dc]) return dc; } return c } // Usage bidirectionalIndexOf(array,'value');
这怎么可能?
我认为在数组中获得reflection索引的简单计算非常简单,比实际循环迭代快两倍。
下面是一个复杂的例子,每次迭代进行三次检查,但是这只能通过一个更长的计算来实现,这会导致代码变慢。
如果您正在重复检查数组中是否存在对象,则应该查看
- 通过在数组中插入sorting来保持数组的sorting(将新对象放在正确的位置)
- 使更新对象为删除+sorting插入操作和
- 在你的
contains(a, obj)
使用二进制search查找。
function inArray(elem,array) { var len = array.length; for(var i = 0 ; i < len;i++) { if(array[i] == elem){return i;} } return -1; }
如果find,则返回数组索引,否则返回-1
我们使用这个片段(使用对象,数组,string):
/* * @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
尽pipearray.indexOf(x)!=-1
是最简单的方法(非Internet Explorer浏览器已经支持了十多年…),但它不是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})
(不幸的是,虽然你可以创build一个Array.prototype.contains来“冻结”一个数组,并且在这个._cache中存储一个hashtable,但是如果你之后select编辑你的数组的话,这会给出错误的结果。让你保持这个状态,不像Python的例子。)
如果您使用JavaScript 1.6或更高版本(Firefox 1.5或更高版本),则可以使用Array.indexOf 。 否则,我认为你最终会得到类似于你原来的代码的东西。
使用lodash的一些function。
它简洁,准确,有很好的跨平台支持。
被接受的答案甚至不符合要求。
要求:build议最简洁有效的方法来确定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)
ECMAScript 6有一个优雅的寻找build议。
find方法为数组中的每个元素执行一次callback函数,直到findcallback函数返回真值的地方。 如果find这样的元素,find立即返回该元素的值。 否则,find返回undefined。 callback仅针对已赋值的数组的索引进行调用; 对于已被删除或从未被赋值的索引,不会调用它。
这是MDN文档 。
查找function就像这样工作。
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; } }); }
适用于所有现代浏览器的解决scheme:
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 +解决scheme:
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; }; }
用法:
contains([{a: 1}, {a: 2}], {a: 1}); // true
为什么要使用JSON.stringify
?
Array.indexOf
和Array.includes
(以及这里的大多数答案)只能通过引用而不是按值来比较。
[{a: 1}, {a: 2}].includes({a: 1}); // false, because {a: 1} is a new object
奖金
非优化的ES6单线:
[{a: 1}, {a: 2}].some(item => JSON.stringify(item) === JSON.stringify({a: 1)); // true
注意:如果键的顺序相同,按值比较对象会更好,所以为了安全起见,您可以先使用如下所示的包对键进行sorting: https : //www.npmjs.com/package/sort-keys
用perf优化更新了contains
函数。 感谢itinance指出来。
使用:
var myArray = ['yellow', 'orange', 'red'] ; alert(!!~myArray.indexOf('red')); //true
演示
要确切知道在这一点tilde
~
做什么,请参考这个问题代字符在expression式之前做什么? 。
使用:
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()”方法的Set :
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'));
Prototype是这样做的 :
/** * 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; }
在这里也看到他们如何把它挂起来。
正如其他人所提到的,你可以使用Array.indexOf
,但它并不适用于所有的浏览器。 以下是https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf中的代码,以使其在旧版浏览器中的工作方式相同。;
indexOf是ECMA-262标准的新增内容; 因此它可能不会出现在所有浏览器中。 你可以通过在脚本的开头插入下面的代码来解决这个问题,允许在本身不支持的实现中使用indexOf。 该algorithm与ECMA-262第5版中规定的algorithm完全相同,假定Object,TypeError,Number,Math.floor,Math.abs和Math.max具有它们的原始值。
if (!Array.prototype.indexOf) { Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { "use strict"; if (this == null) { throw new TypeError(); } var t = Object(this); var len = t.length >>> 0; if (len === 0) { return -1; } var n = 0; if (arguments.length > 1) { n = Number(arguments[1]); if (n != n) { // shortcut for verifying if it's NaN n = 0; } else if (n != 0 && n != Infinity && n != -Infinity) { n = (n > 0 || -1) * Math.floor(Math.abs(n)); } } if (n >= len) { return -1; } var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); for (; k < len; k++) { if (k in t && t[k] === searchElement) { return k; } } return -1; } }
绝不是最好的,但我只是变得富有创造性,并join了剧目。
不要使用这个
Object.defineProperty(Array.prototype, 'exists', { value: function(element, index) { var index = index || 0 return index === this.length ? -1 : this[index] === element ? index : this.exists(element, ++index) } }) // Outputs 1 console.log(['one', 'two'].exists('two')); // Outputs -1 console.log(['one', 'two'].exists('three')); console.log(['one', 'two', 'three', 'four'].exists('four'));
你也可以使用这个技巧:
var arrayContains = function(object) { return (serverList.filter(function(currentObject) { if (currentObject === object) { return currentObject } else { return false; } }).length > 0) ? true : false }
类似的事情:通过“searchlambda”find第一个元素:
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
同样在咖啡上:
Array.prototype.find = (search_lambda) -> @[@map(search_lambda).indexOf(true)]