Frage

Was ist die präzise und effiziente Methode, um herauszufinden, ob ein JavaScript-Array ein Objekt enthält?

Dies ist der einzige Weg, ich weiß, es zu tun:

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

Gibt es eine bessere und präzisere Art und Weise dies zu erreichen?

Dies ist sehr eng verwandt Überlauf Frage Stack die beste Weg, ein Element in einer JavaScript-Array zu finden? , welche Adressen Objekte in einem Array mit indexOf zu finden.

War es hilfreich?

Lösung

Aktuelle Browser haben Array#includes , die tut genau , die wird weit unterstützt, und hat eine polyfill für älteren Browser.

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

Sie können auch verwenden Array#indexOf , die weniger direkt ist , aber nicht Polyfills für veraltet Browser benötigen.

jQuery bietet $.inArray , die funktional äquivalent zu Array#indexOf ist.

Underscore.js , eine JavaScript-Utility-Bibliothek, Angebote _.contains(list, value) , alias _.include(list, value), welche beide verwenden indexOf intern, wenn ein JavaScript-Array übergeben.

Einige andere Frameworks bieten ähnliche Methoden:

Beachten Sie, dass einige Frameworks implementieren dies als eine Funktion, während andere die Funktion zum Array Prototyp hinzuzufügen.

Andere Tipps

Update: Wie @orip in den Kommentaren erwähnt, wurde die verknüpfte Benchmark im Jahr 2008 durchgeführt, so dass die Ergebnisse nicht für modernen Browser relevant sein können. Allerdings müssen Sie sich wahrscheinlich nicht-moderne Browser sowieso zu unterstützen und sie haben wahrscheinlich nicht seit aktualisiert. Testen Sie sich selbst.

Wie bereits gesagt wurde, ist die Iteration durch das Array wahrscheinlich der beste Weg, aber es gewesen bewiesen dass ein Rückgang der while Schleife ist der schnellste Weg in JavaScript iterieren. So können Sie Ihren Code neu schreiben möchten, wie folgt:

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

Natürlich können Sie auch Array Prototyp erweitern:

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

Und jetzt können Sie einfach verwenden Sie die folgende:

alert([1, 2, 3].contains(2)); // => true
alert([1, 2, 3].contains('2')); // => false

indexOf vielleicht, aber es ist eine „JavaScript-Erweiterung die ECMA-262-Standard;. als solche nicht in anderen Implementierungen des Standards vorhanden sein können "

Beispiel:

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

Microsoft tut nicht eine Art alternative bieten, aber Sie eine ähnliche Funktionalität wie Arrays in Internet Explorer (und andere Browser, die nicht indexOf unterstützen) hinzufügen können, wenn Sie wollen, als schnelle Google-Suche zeigt (zB dies ).

ECMAScript 7 führt Array.prototype.includes .

Es kann wie folgt verwendet werden:

[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false

Er akzeptiert auch ein optionales zweites Argument fromIndex:

[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true

Im Gegensatz zu indexOf, die verwendet strikter Gleichheit Vergleich , vergleicht includes mit SameValueZero Gleichheit Algorithmus. Das bedeutet, dass Sie erkennen können, wenn ein Array mit einer NaN enthält:

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

Auch im Gegensatz zu indexOf, includes sich nicht überspringen Indizes fehlen:

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

Derzeit wird ein Entwurf, es ist nach wie vor kann aber sein

b ist der Wert, und a ist das Array. Es gibt true oder false:

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

Die Top-Antworten übernehmen Urtyp aber wenn Sie möchten, um herauszufinden, ob ein Array ein Objekt mit einer gewissen Eigenschaft enthält, Array.prototype.some () eine sehr elegante Lösung ist:

const items = [ {a: '1'}, {a: '2'}, {a: '3'} ]

items.some(item => item.a === '3')  // returns true
items.some(item => item.a === '4')  // returns false

Das Schöne daran ist, dass die Iteration abgebrochen wird, sobald das Element so unnötige Iterationszyklen gefunden werden gespeichert.

Auch sie paßt gut in einer if Aussage, da sie einen Booleschen Wert zurück:

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

* Wie jamess im Kommentar darauf hingewiesen, wie heute, September 2018 wird Array.prototype.some() voll unterstützt: caniuse.com Auflagetisch

Hier ist ein JavaScript 1.6 kompatibel Implementierung von 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;
        };
}

Verwendung:

function isInArray(array, search)
{
    return array.indexOf(search) >= 0;
}

// Usage
if(isInArray(my_array, "my_value"))
{
    //...
}

Die Erweiterung des JavaScript Array Objekts ist eine wirklich schlechte Idee, weil Sie neue Eigenschaften einzuführen (Ihre benutzerdefinierten Methoden) in for-in Schleifen, die vorhandenen Scripts brechen kann. Vor ein paar Jahren die Autoren der Prototype Bibliothek mussten ihre Bibliothek Implementierung reengineeren nur zu entfernen diese Art der Sache.

Wenn Sie mit anderen JavaScript laufen auf Ihrer Seite über die Kompatibilität kümmern müssen für sie nicht, gehen, sonst würde ich die umständliche empfehlen, aber sichere Funktion Lösung freistehend.

Denken aus der Box für eine Sekunde, wenn man diesem Aufruf viele, viele Male machen, ist es wesentlich effizienter zu verwenden, ein assoziatives Array eine Karte Lookups zu tun, um eine Hash-Funktion verwendet wird.

https://developer.mozilla.org/ en-US / docs / Web / JavaScript / Reference / Global_Objects / Karte

Einzeiler:

function contains(arr, x) {
    return arr.filter(function(elem) { return elem == x }).length > 0;
}

Ich verwende folgende Möglichkeiten:

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 () wurde hinzugefügt auf den ECMA-262-Standard in der 5. Auflage

Ein hoffentlich schneller bidirektionaler indexOf / lastIndexOf Alternative

2015

Während die neue Methode enthält ist sehr schön, die Unterstützung im Grunde Null für jetzt ist.

Es ist lange Zeit, dass ich die Art und Weise zu denken, die langsame indexOf / lastIndexOf Funktionen zu ersetzen.

Eine performante Art und Weise bereits gefunden wurde, an der Spitze Antworten suchen. Von denen, wählte ich die contains Funktion von @Damir Zekic geschrieben, die die schnellsten sein sollten. Aber es gibt auch an, dass die Benchmarks aus dem Jahr 2008 ist und so veraltet ist.

Ich ziehe auch über while for, aber nicht einen bestimmten Grund, warum ich die Funktion mit einem for-Schleife ended zu schreiben. Es könnte auch mit einem while -- erfolgen.

Ich war gespannt, ob die Iteration viel langsamer war, wenn ich beiden Seiten des Arrays überprüfen, während es zu tun. Offenbar nicht, und so ist diese Funktion um zwei mal schneller als die größten Aktionäre gewählt. Natürlich ist es auch schneller als die native ein. Dies in einer realen Umgebung, wo man nie wissen, ob der Wert, den Sie suchen am Anfang oder am Ende des Arrays.

Wenn Sie wissen, dass Sie nur geschoben ein Array mit einem Wert, mit lastIndexOf bleibt wahrscheinlich die beste Lösung, aber wenn man durch große Arrays reisen und das Ergebnis überall sein könnte, könnte dies eine feste Lösung sein, um die Dinge schneller zu machen.

Bidirektionale 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');

Performance-Test

http://jsperf.com/bidirectionalindexof

Als Test habe ich ein Array mit 100k Einträgen.

Drei Anfragen:. Am Anfang, in der Mitte und am Ende des Feldes

Ich hoffe, dass Sie auch diese interessant finden und die Leistung testen.

Hinweis: Wie Sie sehen kann ich leicht die contains Funktion der indexOf & lastIndexOf Ausgang (also im Grunde true mit dem index und false mit -1) zu reflektieren modifiziert. Das sollte es nicht schaden.

Die Array-Prototyp Variante

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

Die Funktion kann auch zurück wahr oder falsch oder sogar das Objekt leicht modifiziert werden, String oder was auch immer es ist.

Und hier ist die while Variante:

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

Wie ist das möglich?

Ich denke, dass die einfache Berechnung den reflektierten Index in einem Array zu erhalten, so einfach ist, dass es zwei Mal schneller als eine tatsächliche Schleifeniterationslatenzzeit tun.

Hier ist ein komplexeres Beispiel drei Prüfungen pro Iteration zu tun, aber das ist nur möglich mit einer längeren Berechnung, die die Verlangsamung des Codes verursacht.

http://jsperf.com/bidirectionalindexof/2

Wenn Sie mit JavaScript 1.6 oder höher (Firefox 1.5 oder höher) können Sie verwenden Array.indexOf . Ansonsten halte ich Dich mit etwas ähnlichem zu Ihrem ursprünglichen Code, um am Ende gehen.

Wenn Sie immer wieder auf die Existenz eines Objekts in einem Array Überprüfung sollten Sie vielleicht schauen Sie in

  1. das Array jederzeit sortiert Keeping von Einfügung tun Art in Ihrem Array (setzen neue Objekte in an der richtigen Stelle)
  2. Stellen Sie Objekte als remove + sortierten Einfügevorgang aktualisieren und
  3. Verwenden Sie ein binäre Suche Nachschlag in Ihrer 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;
} 

Gibt Array-Index, wenn gefunden, oder -1, wenn nicht gefunden

Wir verwenden diese Schnipsel (arbeitet mit Objekten, Arrays, Strings):

/*
 * @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
});

Verwendung:

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

Mit lodash einiger Funktion.

Es ist prägnant, präzise und hat eine großen Cross-Plattform-Support.

Die akzeptierte Antwort nicht erfüllt auch die Anforderungen.

Anforderungen:. Empfehlung prägnanteste und effiziente Methode, um herauszufinden, ob ein JavaScript-Array ein Objekt enthält

akzeptierte Antwort:

$.inArray({'b': 2}, [{'a': 1}, {'b': 2}])
> -1

Meine Empfehlung:

_.some([{'a': 1}, {'b': 2}], {'b': 2})
> true

Weitere Informationen:

$. InArray funktioniert gut für die Bestimmung, ob ein Skalar Wert existiert in einer Reihe von Skalaren ...

$.inArray(2, [1,2])
> 1

... aber die Frage stellt klar für eine effiziente Art und Weise zu bestimmen, ob ein Objekt in einem Array enthalten ist.

Um beiden Skalare und Objekte zu behandeln, könnten Sie dies tun:

(_.isObject(item)) ? _.some(ary, item) : (_.indexOf(ary, item) > -1)

Lösung, die in allen modernen Browsern funktioniert:

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

Verbrauch:

contains([{a: 1}, {a: 2}], {a: 1}); // true

IE6 + Lösung:

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

Verbrauch:

contains([{a: 1}, {a: 2}], {a: 1}); // true

Warum JSON.stringify benutzen?

Array.indexOf und Array.includes (sowie die meisten Antworten hier) nur durch Bezugnahme vergleichen und nicht von Wert.

[{a: 1}, {a: 2}].includes({a: 1});
// false, because {a: 1} is a new object

Bonus

Nicht optimierte ES6 Einzeiler:

[{a: 1}, {a: 2}].some(item => JSON.stringify(item) === JSON.stringify({a: 1));
// true

Hinweis: Vergleicht man Objekte Wert wird besser funktionieren, wenn die Schlüssel in der gleichen Reihenfolge sind, so sicher sein, Sie könnten die Schlüssel sortiert wird zuerst mit einem Paket wie diese: https://www.npmjs.com/package/sort-keys


die contains Funktion mit einer perf Optimierung aktualisiert. Dank itinance für den Hinweis it out.

Während array.indexOf(x)!=-1 die knappste Art und Weise, dies zu tun (und wird von Nicht-Internet Explorer-Browser seit über zehn Jahren unterstützt ...), ist es nicht O (1), sondern O (N), das ist schrecklich . Wenn Ihr Array nicht wird ändern, können Sie Ihr Array in einer Hash-Tabelle konvertieren, dann tun table[x]!==undefined oder ===undefined:

Array.prototype.toTable = function() {
    var t = {};
    this.forEach(function(x){t[x]=true});
    return t;
}

Demo:

var toRemove = [2,4].toTable();
[1,2,3,4,5].filter(function(x){return toRemove[x]===undefined})

(Leider, während Sie eine Array.prototype.contains erstellen „einzufrieren“ ein Array und speichern Sie eine Hash-Tabelle in this._cache in zwei Linien, würde dies falsche Ergebnisse, wenn Sie gewählt haben das Array später zu bearbeiten. JavaScript ist nicht genügend Haken Sie läßt diesen Zustand erhalten, im Gegensatz zu Python zum Beispiel).

ECMAScript 6 hat einen eleganten Vorschlag auf Entdeckung.

  

Die find-Methode führt die Callback-Funktion einmal für jedes Element   die in dem Array, bis er einen findet, wo einen Rückruf true zurück   Wert. Wenn ein solches Element gefunden wird, finden sofort gibt den Wert   dieses Element. Ansonsten finden undefined zurück. Rückruf   nur für Indizes des Arrays aufgerufen, die Werte haben, zugeordnet sind; es   ist nicht für die Indizes aufgerufen, die gelöscht wurden oder die noch nie   wurden Werte zugewiesen.

Hier ist der MDN-Dokumentation auf das.

Der Fund Funktionalität funktioniert wie folgt.

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

Sie können diese verwenden in ECMAScript 5 und unterhalb von Definition der Funktion .

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

Verwendung:

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

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

Demo

Um genau zu wissen, was der tilde ~ an dieser Stelle tun, auf diese Frage finden Sie Was macht ein Tilde tun, wenn es einen Ausdruck vorangestellt? .

Hier ist, wie Prototyp tut es :

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

Siehe auch hier denn wie sie es anschließen.

OK, können Sie einfach optimieren Sie Ihren Code, um das Ergebnis zu erhalten!

Es gibt viele Möglichkeiten, dies die sind sauberer und besser zu machen, aber ich wollte nur Ihre Muster erhalten und auf diese mit JSON.stringify gelten, einfach nur so etwas wie dies in Ihrem Fall tun:

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

Verwendung:

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

Ich weiß, es ist nicht der beste Weg zu gehen, aber da gibt es keinen native IComparable Weg zwischen Objekten zu interagieren ist, ich denke, das ist so nah, wie Sie zwei Einheiten in einem Array vergleichen bekommen können. Auch Array-Objekt erstreckt möglicherweise nicht eine kluge Sache zu tun, aber manchmal OK, es ist (wenn Sie sich dessen bewusst sind und die Trade-off).

Man kann benutzen eingestellt, dass die hat Methode "hat ()":

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

Sie können auch diesen Trick verwenden:

var arrayContains = function(object) {
  return (serverList.filter(function(currentObject) {
    if (currentObject === object) {
      return currentObject
    }
    else {
      return false;
    }
  }).length > 0) ? true : false
}
  1. Entweder Verwendung Array.indexOf(Object).
  2. Mit ECMA 7 kann man die Array.includes(Object) verwenden.
  3. Mit 6 ECMA können Sie Array.find(FunctionName) verwenden, wo FunctionName ein Benutzer definierte Funktion für das Objekt in dem Array zu suchen.

    Hope, das hilft!

ähnliche Sache: Sucht das erste Elements durch ein "Suche lambda":

Array.prototype.find = function(search_lambda) {
  return this[this.map(search_lambda).indexOf(true)];
};

Verbrauch:

[1,3,4,5,8,3,5].find(function(item) { return item % 2 == 0 })
=> 4

Same in Coffeescript:

Array.prototype.find = (search_lambda) -> @[@map(search_lambda).indexOf(true)]
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top