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;
}
これを達成するための、より優れた、より簡潔な方法はあるでしょうか?
これはスタック オーバーフローの質問と非常に密接に関連しています JavaScript 配列内の項目を見つける最良の方法は? これは、を使用して配列内のオブジェクトを検索することに対処します。 indexOf
.
解決
現在のブラウザーには、 があります。 Array#includes
、これは正確に、広くサポートされています、 polyfill は古いブラウザ向けです。
> ['joe', 'jane', 'mary'].includes('jane');
true
Array#indexOf
も使用できます。 。これは直接的ではありませんが、古いブラウザにはポリフィルは必要ありません。
jQueryは、機能的に同等の $。inArray
を提供します。 Array#indexOf
へ。
underscore.js は、 _。contains(list、value)
、エイリアス _。include(list、value)
、両方ともJavaScript配列が渡された場合、内部で indexOf を使用します。
他のフレームワークでも同様の方法を提供しています:
- Dojo Toolkit:
dojo.indexOf(array、value、[fromIndex、findLast ])
- プロトタイプ:
array.indexOf(value)
- MooTools:
array.indexOf(value)
- MochiKit:
findValue(array、value )
- MS Ajax:
array.indexOf(value)コード>
- Ext:
Ext.Array .contains(array、value)
- Lodash:
_。includes(array、value、[from])
(4.0.0以前の_。contains
) - ECMAScript 2016: <コード>配列。 include(value)
一部のフレームワークはこれを関数として実装しますが、他のフレームワークは関数を配列プロトタイプに追加します。
他のヒント
更新:@oripがコメントで言及しているように、リンクされたベンチマークは2008年に行われたため、結果は最新のブラウザーに関連しない場合があります。ただし、とにかく非近代的なブラウザをサポートするにはこれが必要であり、それ以降はおそらく更新されていません。常に自分でテストしてください。
他の人が言ったように、配列の反復はおそらく最良の方法ですが、はJavaScriptで反復する while
ループの減少が最速の方法であることが実証されています。したがって、次のようにコードを書き直したい場合があります。
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標準の&quot; JavaScript拡張機能です。そのため、標準の他の実装には存在しない場合があります。&quot;
例:
[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
また、オプションの2番目の引数 fromIndex
を受け入れます:
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
厳密な等価比較、 includes
は SameValueZero 等式アルゴリズム。つまり、配列に NaN
:
[1, 2, NaN].includes(NaN); // true
また、 indexOf
とは異なり、 includes
は欠落しているインデックスをスキップしません:
new Array(5).includes(undefined); // true
現在はまだドラフトですが、ポリフィルを使用して、すべてのブラウザで動作するようにします。
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
ループに新しいプロパティ(カスタムメソッド)を導入するため、本当に悪い考えです。数年前、プロトタイプライブラリの作成者は、ライブラリの実装をリエンジニアリングして、この種のもの。
ページで実行されている他のJavaScriptとの互換性について心配する必要がない場合は、それを選択します。そうでない場合は、より厄介ですが、より安全な自立関数ソリューションをお勧めします。
少しの間、この呼び出しを何度も行う場合、ハッシュ関数を使用して検索を行うために、マップの連想配列を使用する方がはるかに効率的です。
https://developer.mozilla.org/ en-US / docs / Web / JavaScript / Reference / Global_Objects / Map
ワンライナー:
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
新しいメソッド includes とてもいいです。今のところサポートは基本的にゼロです。
遅いindexOf / lastIndexOf関数を置き換える方法を考えていたのは長い時間です。
上位の回答を見て、パフォーマンスの良い方法がすでに見つかりました。それらの中から、@ Damir Zekicが投稿した contains
関数を選択しました。しかし、ベンチマークは2008年のものであるため、時代遅れであるとも述べています。
for
よりも while
の方が好きですが、特定の理由ではなく、forループで関数の作成を終了しました。 while-
でも実行できます。
実行中に配列の両側をチェックすると、反復がずっと遅くなるのではないかと興味がありました。どうやらいいえ、したがって、この関数は、トップの投票のものよりも約2倍高速です。明らかに、ネイティブのものよりも高速です。これは、検索している値が配列の先頭にあるのか末尾にあるのかがわからない現実の環境で。
値を使用して配列をプッシュしたことがわかっている場合、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
テストとして、10万エントリの配列を作成しました。
3つのクエリ:最初、中央、および配列の最後。
これもおもしろいと思い、パフォーマンスをテストしてください。
注:ご覧のように、 contains
関数をindexOf&amp;を反映するように少し変更しました。 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を返すように簡単に変更することもできますし、オブジェクト、文字列、またはそれが何であっても返します。
また、 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');
これはどのように可能ですか?
配列に反映されたインデックスを取得するための単純な計算は非常に単純で、実際のループ反復を行うよりも2倍高速だと思います。
反復ごとに3つのチェックを行う複雑な例がありますが、これは、コードの速度低下を引き起こすより長い計算でのみ可能です。
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の 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)
最新のすべてのブラウザで動作するソリューション:
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
パフォーマンス最適化を備えた機能。ありがとう 旅程 それを指摘してくれて。
array.indexOf(x)!=-1
はこれを行うための最も簡潔な方法ですが(インターネット以外のエクスプローラブラウザで10年以上サポートされています...)、 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に2行で保存できますが、後で配列を編集することを選択した場合、これは間違った結果をもたらします。たとえばPythonとは異なり、この状態を維持するためのフックが不十分です。)
ECMAScript 6には、検索に関するエレガントな提案があります。
findメソッドは、要素ごとにコールバック関数を1回実行します コールバックが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
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
〜
が何を行うかを正確に知るには、この質問を参照してください 式の前にあるチルダは何をしますか? 。
プロトタイプがそれを行う方法 a>:
/**
* 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;
}
こちらもご覧ください。彼らがそれを接続する方法について。
OK、コードを最適化するだけで結果を得ることができます!
これを行うには、よりクリーンで優れた方法が多数ありますが、パターンを取得し、 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の方法がないため、これは配列内の2つのエンティティを比較できる限り近いと思います。また、Arrayオブジェクトを拡張することは賢明なことではないかもしれませんが、場合によっては問題ないことがあります(そのこととトレードオフを認識している場合)。
Set を使用して、メソッド&quot; has()&quot;:
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
はユーザーです 配列内のオブジェクトを検索するための定義済み関数。これがお役に立てば幸いです!
同様のこと:&quot; search lambda&quot;で最初の要素を見つけます:
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)]