JavaScript で配列にオブジェクトが含まれているかどうかを確認するにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/237104

質問

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 へ。

JavaScriptユーティリティライブラリである

underscore.js は、 _。contains(list、value) 、エイリアス _。include(list、value)、両方ともJavaScript配列が渡された場合、内部で indexOf を使用します。

他のフレームワークでも同様の方法を提供しています:

一部のフレームワークはこれを関数として実装しますが、他のフレームワークは関数を配列プロトタイプに追加します。

他のヒント

更新:@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つのチェックを行う複雑な例がありますが、これは、コードの速度低下を引き起こすより長い計算でのみ可能です。

http://jsperf.com/bidirectionalindexof/2

JavaScript 1.6以降(Firefox 1.5以降)を使用している場合は、 Array.indexOf 。そうしないと、元のコードに似たものになってしまうと思います。

配列内のオブジェクトの存在を繰り返し確認する場合は、おそらく調べる必要があります

  1. 配列に挿入ソートを実行して、常に配列をソートします(新しいオブジェクトを配置します)適切な場所で)
  2. 更新オブジェクトをremove + sorted insert操作として作成する
  3. contains(a、obj)でバイナリ検索ルックアップを使用します
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 &mdash; 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
}
  1. Array.indexOf(Object)を使用します。
  2. ECMA 7では、 Array.includes(Object)を使用できます。
  3. 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)]
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top