找出 JavaScript 数组是否包含值的最简洁有效的方法是什么?
这是我知道的唯一方法:
function contains(a, obj) {
for (var i = 0; i < a.length; i++) {
if (a[i] === obj) {
return true;
}
}
return false;
}
有没有更好更简洁的方法来实现这一点?
这与 Stack Overflow 问题 Best way to find an item in a JavaScript Array? 密切相关,该问题解决了使用 indexOf
在数组中查找对象的问题。
~[1,2,3].indexOf(4)
将返回 0,其评估结果为 false,而 ~[1,2,3].indexOf(3)
将返回 -3,其评估结果为 true。
~
不是您想要用来转换为布尔值的,因为您需要 !
。但在这种情况下,您想检查是否与 -1 相等,因此函数可能 endreturn [1,2,3].indexOf(3) === -1;
~
不是二进制,它将单独反转值的每一位。
[1,2,3].indexOf(4)
实际上会return -1。正如@mcfedr 指出的那样,~
是 bitwise-NOT operator,请参阅 ES5 11.4.8。问题是,由于 -1
的二进制表示仅由 1 组成,它的补码是 0
,其评估结果为假。任何其他数字的补数都将是非零的,因此是正确的。因此,~
工作得很好,并且经常与 indexOf
一起使用。
[[1,2],[3,4]].includes([3,4])
在哪里?
现代浏览器具有 Array#includes
,它完全做到这一点,并且 is widely supported 被除 IE 之外的所有人:
console.log(['joe', 'jane', 'mary'].includes('jane')); //真的
您也可以使用 Array#indexOf
,它不太直接,但对于过时的浏览器不需要 polyfill。
console.log(['joe', 'jane', 'mary'].indexOf('jane') >= 0); //真的
许多框架也提供了类似的方法:
jQuery: $.inArray(value, array, [fromIndex])
Underscore.js:_.contains(array, value)(也别名为 _.include 和 _.includes)
Dojo 工具包:dojo.indexOf(array, value, [fromIndex, findLast])
原型:array.indexOf(value)
MooTools:array.indexOf(值)
MochiKit:findValue(数组,值)
MS Ajax:array.indexOf(value)
分机:Ext.Array.contains(数组,值)
Lodash: _.includes(array, value, [from]) (是 _.contains 之前的 4.0.0)
Ramda:R.includes(值,数组)
请注意,一些框架将其实现为一个函数,而另一些框架则将该函数添加到数组原型中。
2019 年更新:此答案来自 2008 年(11 岁!),与现代 JS 使用无关。承诺的性能改进是基于当时在浏览器中完成的基准测试。它可能与现代 JS 执行上下文无关。如果您需要一个简单的解决方案,请寻找其他答案。如果您需要最佳性能,请在相关执行环境中为自己进行基准测试。
正如其他人所说,通过数组进行迭代可能是最好的方法,但是 has been proven 递减 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 Microsoft does not offer some kind of alternative,但如果您愿意,可以将类似的功能添加到 Internet Explorer(以及不支持 indexOf
的其他浏览器)中的数组,作为 quick Google search reveals(例如,this one)。
最佳答案假定原始类型,但如果您想了解数组是否包含具有某些特征的 object,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 support table
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
与使用 Strict Equality Comparison 的 indexOf
不同,includes
使用 SameValueZero 相等算法进行比较。这意味着您可以检测数组是否包含 NaN
:
[1, 2, NaN].includes(NaN); // true
与 indexOf
不同的是,includes
不会跳过缺失的索引:
new Array(5).includes(undefined); // true
可以是 polyfilled 使其适用于所有浏览器。
假设您已经定义了一个这样的数组:
const array = [1, 2, 3, 4]
以下是检查其中是否存在 3
的三种方法。它们都返回 true
或 false
。
Native Array 方法(自 ES2016 起)(兼容性表)
array.includes(3) // true
作为自定义 Array 方法(ES2016 之前)
// Prefixing the method with '_' to avoid name clashes
Object.defineProperty(Array.prototype, '_includes', { value: function (v) { return this.indexOf(v) !== -1 }})
array._includes(3) // true
简单的功能
const includes = (a, v) => a.indexOf(v) !== -1
includes(array, 3) // true
这是 Array.indexOf
的 JavaScript 1.6 compatible 实现:
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;
};
}
[].indexOf
是 Array.prototype.indexOf
的简写。我们偏执的 Javascript 程序员不惜一切代价避免扩展原生原型。
[].indexOf
不是创建一个新数组然后访问 indexOf
,而 Array.prototype.indexOf
只是直接访问原型吗?
[].indexOf === Array.prototype.indexOf
(在 FireBug 中尝试),但反过来 [].indexOf !== Array.indexOf
。
利用:
function isInArray(array, search)
{
return array.indexOf(search) >= 0;
}
// Usage
if(isInArray(my_array, "my_value"))
{
//...
}
x ? true : false
通常是多余的。是这里。
array.indexOf(search) >= 0
已经是一个布尔值。只需 return array.indexOf(search) >= 0
。
扩展 JavaScript Array
对象是一个非常糟糕的主意,因为您将新属性(您的自定义方法)引入到 for-in
循环中,这可能会破坏现有脚本。几年前,Prototype 库的作者不得不重新设计他们的库实现以删除这种东西。
如果您不需要担心与页面上运行的其他 JavaScript 的兼容性,那就去吧,否则,我会推荐更笨拙但更安全的独立函数解决方案。
表现
今天 2020.01.07 我在 Chrome v78.0.0、Safari v13.0.4 和 Firefox v71.0.0 上对 MacOs HighSierra 10.13.6 进行了 15 种选定解决方案的测试。结论
基于 JSON、Set 和意外发现 (K,N,O) 的解决方案在所有浏览器上最慢
es6 包含 (F) 仅在 chrome 上速度很快
基于 for (C,D) 和 indexOf (G,H) 的解决方案在大小数组上的所有浏览器上都非常快,因此它们可能是有效解决方案的最佳选择
循环期间索引减少的解决方案,(B)较慢可能是因为 CPU 缓存的方式起作用。
当搜索的元素位于数组长度的 66% 位置时,我还对大数组进行了测试,基于 for (C,D,E) 的解决方案给出了类似的结果(~630 ops/sec - 但 safari 和 firefox 上的 E 为 10 -20% 比 C 和 D 慢)
结果
https://i.stack.imgur.com/YPIG1.png
细节
我执行 2 个测试用例:对于具有 10 个元素的数组和具有 100 万个元素的数组。在这两种情况下,我们都将搜索到的元素放在数组中间。
让 log = (name,f) => console.log(`${name}: 3-${f(arr,'s10')} 's7'-${f(arr,'s7')} 6- ${f(arr,6)} 's3'-${f(arr,'s3')}`) 让 arr = [1,2,3,4,5,'s6','s7','s8 ','s9','s10']; //arr = new Array(1000000).fill(123); arr[500000]=7;函数 A(a, val) { var i = -1; var n = a.length; while (i++
小数组 - 10 个元素
您可以在您的机器上执行测试 HERE
https://i.stack.imgur.com/gDDCp.png
大数组 - 1.000.000 个元素
您可以在您的机器上执行测试 HERE
https://i.stack.imgur.com/cTL3s.png
单线:
function contains(arr, x) {
return arr.filter(function(elem) { return elem == x }).length > 0;
}
array.filter(e=>e==x).length > 0
等价于 array.some(e=>e==x)
但 some
更有效
开箱即用,如果您多次多次进行此调用,则使用关联数组和 Map 使用散列函数进行查找会效率更高。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
我使用以下内容:
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 标准中
contains = (a, obj) => a.some((element) => element === obj))
希望更快的双向 indexOf
/ lastIndexOf
替代方案
2015
虽然新方法 includes
非常好,但目前支持率基本上为零。
很长一段时间以来,我一直在想一种方法来替换慢速 indexOf
/lastIndexOf
函数。
已经找到了一种高效的方法,查看最重要的答案。从那些我选择了@Damir Zekic 发布的 contains
函数,它应该是最快的。但它也指出,基准是从 2008 年开始的,因此已经过时。
我也更喜欢 while
而不是 for
,但由于没有特定原因,我结束了使用 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');
性能测试
作为测试,我创建了一个包含 100k 条目的数组。
三个查询:在数组的开头、中间和结尾。
我希望你也觉得这很有趣并测试性能。
注意:如您所见,我稍微修改了 contains
函数以反映 indexOf
& 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');
该函数也可以很容易地修改为返回真或假,甚至返回对象、字符串或其他任何东西。
这是 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');
这怎么可能?
我认为在数组中获取反射索引的简单计算非常简单,它比执行实际循环迭代快两倍。
这是一个复杂的示例,每次迭代执行三项检查,但这只有通过更长的计算才能实现,这会导致代码变慢。
https://web.archive.org/web/20151019160219/http://jsperf.com/bidirectionalindexof/2
function inArray(elem,array)
{
var len = array.length;
for(var i = 0 ; i < len;i++)
{
if(array[i] == elem){return i;}
}
return -1;
}
如果找到则返回数组索引,如果未找到则返回 -1
如果您反复检查数组中是否存在对象,您可能应该查看
通过在数组中进行插入排序(将新对象放在正确的位置)来保持数组始终排序(将新对象放在正确的位置)将更新对象作为删除+排序的插入操作,并在您的 contains(a, obj) 中使用二进制搜索查找。
我们使用这个片段(适用于对象、数组、字符串):
/*
* @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
适用于所有现代浏览器的解决方案:
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;
};
}
用法:
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
注意:如果键的顺序相同,按值比较对象会更好,因此为了安全起见,您可以先使用如下包对键进行排序:https://www.npmjs.com/package/sort-keys
使用性能优化更新了 contains
函数。感谢itinance指出。
使用 lodash 的 some 函数。
它简洁,准确,并具有出色的跨平台支持。
接受的答案甚至不符合要求。
要求:推荐最简洁有效的方法来判断一个 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)
此要求的简单解决方案是使用 find()
如果您有如下对象数组,
var users = [{id: "101", name: "Choose one..."},
{id: "102", name: "shilpa"},
{id: "103", name: "anita"},
{id: "104", name: "admin"},
{id: "105", name: "user"}];
然后您可以检查具有您的值的对象是否已经存在:
let data = users.find(object => object['id'] === '104');
如果数据为空,则没有管理员,否则它将返回现有对象,例如:
{id: "104", name: "admin"}
然后您可以在数组中找到该对象的索引并使用代码替换该对象:
let indexToUpdate = users.indexOf(data);
let newObject = {id: "104", name: "customer"};
users[indexToUpdate] = newObject;//your new object
console.log(users);
您将获得如下价值:
[{id: "101", name: "Choose one..."},
{id: "102", name: "shilpa"},
{id: "103", name: "anita"},
{id: "104", name: "customer"},
{id: "105", name: "user"}];
ECMAScript 6 在 find 上有一个优雅的提议。
find 方法对数组中存在的每个元素执行一次回调函数,直到找到一个回调函数返回真值。如果找到这样的元素,find 立即返回该元素的值。否则,find 返回 undefined。回调仅针对已分配值的数组索引调用;对于已被删除或从未被赋值的索引,它不会被调用。
这是关于它的MDN documentation。
查找功能是这样工作的。
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
您可以通过 defining the function 在 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;
}
});
}
虽然 array.indexOf(x)!=-1
是执行此操作的最简洁的方法(并且已被非 Internet 浏览器支持十多年...),但它不是 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 中的两行中,但如果您选择稍后编辑您的数组,这将给出错误的结果。JavaScript 没有足够的钩子来让你保持这种状态,不像 Python 例如。)
可以使用具有“has()”方法的 Set:
function contains(arr, obj) { var proxy = new Set(arr); if (proxy.has(obj)) 返回真;否则返回假; } var arr = ['Happy', 'New', 'Year']; console.log(contains(arr, 'Happy'));
return proxy.has(obj)
比这里有 if-else 语句的两行要干净得多
function contains(arr, obj) { return new Set(arr).has(obj); }
利用:
var myArray = ['yellow', 'orange', 'red'] ;
alert(!!~myArray.indexOf('red')); //true
要确切了解 tilde
~
在这一点上做了什么,请参阅这个问题What does a tilde do when it precedes an expression?。
好的,你可以优化你的代码来得到结果!
有很多方法可以做到这一点更清洁更好,但我只是想获得您的模式并使用 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;
}
contains([{ a: 1, b: 2 }], { b: 2, a: 1 })
,因为字符串化对象保持属性的顺序。
惊讶于这个问题仍然没有添加最新的语法,增加了我的 2 美分。
假设我们有一个对象数组 arrObj,我们想在其中搜索 obj。
Array.prototype.indexOf ->(返回索引或-1)通常用于查找数组中元素的索引。这也可以用于搜索对象,但仅在您传递对同一对象的引用时才有效。
let obj = { name: 'Sumer', age: 36 };
let arrObj = [obj, { name: 'Kishor', age: 46 }, { name: 'Rupen', age: 26 }];
console.log(arrObj.indexOf(obj));// 0
console.log(arrObj.indexOf({ name: 'Sumer', age: 36 })); //-1
console.log([1, 3, 5, 2].indexOf(2)); //3
Array.prototype.includes -> (返回真或假)
console.log(arrObj.includes(obj)); //true
console.log(arrObj.includes({ name: 'Sumer', age: 36 })); //false
console.log([1, 3, 5, 2].includes(2)); //true
Array.prototype.find ->(接受回调,返回第一个在 CB 中返回 true 的值/对象)。
console.log(arrObj.find(e => e.age > 40)); //{ name: 'Kishor', age: 46 }
console.log(arrObj.find(e => e.age > 40)); //{ name: 'Kishor', age: 46 }
console.log([1, 3, 5, 2].find(e => e > 2)); //3
Array.prototype.findIndex ->(接受回调,返回在 CB 中返回 true 的第一个值/对象的索引)。
console.log(arrObj.findIndex(e => e.age > 40)); //1
console.log(arrObj.findIndex(e => e.age > 40)); //1
console.log([1, 3, 5, 2].findIndex(e => e > 2)); //1
由于 find 和 findIndex 接受回调,我们可以通过创造性地设置 true 条件从数组中获取任何对象(即使我们没有引用)。
它有一个参数:一组对象数。数组中的每个对象都有两个整数属性,分别用 x 和 y 表示。该函数必须返回数组中满足 numbers.x == numbers.y
的所有此类对象的计数
var numbers = [ { x: 1, y: 1 }, { x: 2, y: 3 }, { x: 3, y: 3 }, { x: 3, y: 4 }, { x: 4, y : 5 } ];变量计数 = 0; var n = numbers.length; for (var i =0;i
for (var i = 0; i < n; i++) { if (numbers[i].x == (numbers[i] + 1).x) { count += 1; } }
函数 countArray(originalArray) { var 压缩 = []; // 复制输入数组 var copyArray = originalArray.slice(0); // 第一个循环遍历每个元素 for (var i = 0; i < originalArray.length; i++) { var count = 0; // 遍历副本中的每个元素,看看它是否相同 for (var w = 0; w < copyArray.length; w++) { if (originalArray[i] == copyArray[w]) { // 增加数量找到重复的次数计数++; // 将 item 设置为 undefined delete copyArray[w]; } } if (count > 0) { var a = new Object(); a.value = originalArray[i]; a.count = 计数;压缩.push(a); } } 返回压缩; }; // 它应该是这样的: var testArray = new Array("dog", "dog", "cat", "buffalo", "wolf", "cat", "tiger", "cat"); var newArray = countArray(testArray);控制台.log(newArray);
Array.include
array.indexOf(object) != -1
inArray
是返回元素索引的函数的糟糕名称,如果不存在则返回-1
。我希望返回一个布尔值。