連想配列 - javascript 配列 重複 チェック




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

ECMAScript 6は、findに関する上品な提案をしています。

findメソッドは、配列に存在する各要素に対してコールバック関数を1回実行します。コールバック関数は、コールバックが真の値を返す要素を見つけるまで1回実行されます。 そのような要素が見つかると、findはその要素の値をすぐに返します。 そうでなければ、findはundefinedを返します。 コールバックは、値が割り当てられた配列のインデックスに対してのみ呼び出されます。 削除されたか、または値が割り当てられていない索引に対しては呼び出されません。

その上にMDNのドキュメントがあります。

find機能はこのように機能します。

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;
    }
  });
}

JavaScript配列にオブジェクトが含まれているかどうかを調べる最も簡潔で効率的な方法は何ですか?

これが私が知る唯一の方法です:

function contains(a, obj) {
    for (var i = 0; i < a.length; i++) {
        if (a[i] === obj) {
            return true;
        }
    }
    return false;
}

これを達成するためのより良い、より簡潔な方法がありますか?

これは、スタックオーバーフローの問題と非常に密接に関連しています。JavaScript配列内のアイテムを見つける最良の方法は? これは、 indexOfを使用して配列内のオブジェクトを検索することに対処します。


ECMAScript 7はarray.includes(value)導入していarray.includes(value)

これは次のように使用できます:

[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

Strict Equality Comparisonを使用するindexOfとは異なり、 SameValueZero等価アルゴリズムを使用した比較includesます。 つまり、配列にNaN含まれているかどうかを検出できます。

[1, 2, NaN].includes(NaN); // true

また、 indexOfincludes異なり、 includesしていないインデックスをスキップしません:

new Array(5).includes(undefined); // true

現在のところまだドラフトですが、すべてのブラウザで動作するようにpolyfillにすることができます。


JavaScript Arrayオブジェクトを拡張することは、既存のスクリプトを破る可能性のあるfor-inループに新しいプロパティ(独自のメソッド)を導入するため、本当に悪い考えです。 数年前、 Prototypeライブラリの作者は、この種のものを削除するために、ライブラリの実装を再設計しなければなりませんでした。

あなたのページで実行されている他のJavaScriptとの互換性について心配する必要がない場合は、それを行ってください。そうでなければ、より扱いにくいが、より安全な自立機能ソリューションをお勧めします。


OK、コードを最適化して結果を得ることができます 。 これを行うには多くの方法がありますが、よりJSON.stringifyされていますが、私はあなたのパターンを取得し、 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;
}

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 &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;
}

また、彼らがどのようにそれをフックするかについてはhereを参照してください。


indexOfかもしれませんが、それは "ECMA-262標準へのJavaScript拡張です。標準の他の実装では存在しないかもしれません。"

例:

[1, 2, 3].indexOf(1) => 0
["foo", "bar", "baz"].indexOf("bar") => 1
[1, 2, 3].indexOf(4) => -1

AFAICS マイクロソフトではこれに代わるものは何提供していませが、Internet Explorer(およびindexOfサポートしていない他のブラウザ)の配列に同様の機能を追加することができます。 1 )。


array.indexOf(x)!=-1はこれを行うための最も簡潔な方法ですが(Internet Explorer以外のブラウザでは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と違って、この状態を保つようにしてください。)


bは値、 bは配列です。 trueまたはfalse返しtrue

function(a, b) {
    return a.indexOf(b) != -1
}

アップデート:@oripがコメントで言及しているように、リンクされたベンチマークは2008年に行われたので、結果は最新のブラウザには関連しないかもしれません。 しかし、おそらく近代的ではないブラウザーをサポートするためにはこれが必要になるでしょう。おそらく更新されていない可能性があります。 常にあなた自身のためにテストしてください。

他の人が言っているように、配列を使った反復がおそらく最善の方法ですが、 whileループを減らすことがJavaScriptで最も速く反復することが証明されています。 だから、次のようにコードを書き直したいかもしれません:

function contains(a, obj) {
    var i = a.length;
    while (i--) {
       if (a[i] === obj) {
           return true;
       }
    }
    return false;
}

もちろん、配列プロトタイプを拡張することもできます:

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


このトリックを使うこともできます:

var arrayContains = function(object) {
  return (serverList.filter(function(currentObject) {
    if (currentObject === object) {
      return currentObject
    }
    else {
      return false;
    }
  }).length > 0) ? true : 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オブジェクトを拡張することは賢明ではないかもしれませんが、時にはそれは問題ありません。


つかいます:

var myArray = ['yellow', 'orange', 'red'] ;

alert(!!~myArray.indexOf('red')); //true

Demo

この時点でtilde ~が何をするのかを正確に知るには、この疑問を参照してください。 チルダは式の前に何をしますか?


メソッド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'));

上の回答はプリミティブ型を前提としていますが、配列にある特性を持つオブジェクトが含まれているかどうかを調べたい場合、 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

いいことは、要素が見つかると反復が中止され、不要な反復サイクルが保存されるということです。

また、booleanを返すので、 if文にうまく収まります:

if (items.some(item => item.a === '3')) {
  // do something
}

*コメントの中で不満が指摘されたため、2018年9月現在、 Array.prototype.some()は完全にサポートされています: caniuse.comサポートテーブル


他の人が触れたように、 Array.indexOfを使うことができますが、すべてのブラウザで利用できるわけではありません。 https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOfのコードは、古いブラウザでも同じように動作します。

indexOfは、ECMA-262標準への最近の追加です。 それはすべてのブラウザに存在しない可能性があります。 この問題を回避するには、スクリプトの先頭に次のコードを挿入し、ネイティブにサポートしていない実装でindexOfを使用できるようにします。 このアルゴリズムは、Object、TypeError、Number、Math.floor、Math.abs、およびMath.maxが元の値であると仮定して、ECMA-262、第5版で指定されたアルゴリズムとまったく同じです。

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;
    }
}

現代のすべてのブラウザで動作するソリューション:

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.indexOfArray.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 : https://www.npmjs.com/package/sort-keys

perf関数を使ってcontains関数を更新しました。 itinanceを指摘していただきありがとうございます。


現在のブラウザには、 Array#includesていArray#includes 。これは広くサポートされており、古いブラウザ用のpolyfillpolyfillています。

> ['joe', 'jane', 'mary'].includes('jane');
true 

Array#indexOf使用することもできます。これは直感的ではありませんが、最新のブラウザではPolyfillを使用する必要はありません。

jQueryは$.inArray提供しています。これは、 Array#indexOfと機能的に同等です。

underscore.js (JavaScriptユーティリティライブラリ)は、 _.contains(list, value) 、別名_.include(list, value)提供します。どちらもJavaScript配列を渡すと内部的にindexOfを使用します。

他のフレームワークでも同様の方法があります。

いくつかのフレームワークはこれを関数として実装し、他のフレームワークは配列プロトタイプに関数を追加することに注意してください。


配列内にオブジェクトが存在するかどうかを繰り返し確認している場合は、

  1. あなたの配列に挿入ソートを行うことによって配列を常にソートしたままにする(新しいオブジェクトを適切な場所に配置する)
  2. オブジェクトをremove +ソートされた挿入操作として更新する
  3. contains(a, obj)バイナリ検索ルックアップを使用します。

function contains(a, obj) {
    return a.some(function(element){return element == obj;})
}

Array.prototype.some()は、第5版のECMA-262標準に追加されました







browser