Question

Quel est le moyen le plus concis et le plus efficace de savoir si un tableau JavaScript contient un objet?

C’est le seul moyen que je connaisse pour le faire:

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

Existe-t-il un moyen plus efficace et plus concis d’y parvenir?

Ceci est très étroitement lié à la question de débordement de pile Meilleur moyen de rechercher un élément dans un tableau JavaScript? qui permet de rechercher des objets dans un tableau à l'aide de indexOf .

Était-ce utile?

La solution

Les navigateurs actuels ont Le tableau # inclut , ce qui fait exactement cela, est largement pris en charge et possède un polyfill pour les anciens navigateurs.

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

Vous pouvez également utiliser Array # indexOf , ce qui est moins direct, mais ne nécessite pas Polyfills pour les navigateurs obsolètes.

jQuery offre le $. inArray , qui est fonctionnellement équivalent. to Array # indexOf .

underscore.js , une bibliothèque d'utilitaires JavaScript, propose _. contient (liste, valeur) , alias _. include (liste, valeur) , les deux Utiliser indexOf en interne si un tableau JavaScript a été passé.

Certains autres frameworks proposent des méthodes similaires:

Notez que certains frameworks l'implémentent en tant que fonction, tandis que d'autres l'ajoutent au prototype de tableau.

Autres conseils

Mise à jour: Comme @orip mentionne dans les commentaires, le repère lié a été défini en 2008, de sorte que les résultats peuvent ne pas être pertinents pour les navigateurs modernes. Cependant, vous en aurez probablement besoin pour prendre en charge des navigateurs non-modernes et ils n’auront probablement pas été mis à jour depuis. Toujours tester par vous-même.

Comme d'autres l'ont déjà dit, l'itération du tableau est probablement la meilleure solution, mais elle a prouvé qu'une boucle while décroissante est le moyen le plus rapide d'itérer en JavaScript. Donc, vous voudrez peut-être réécrire votre code comme suit:

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

Bien sûr, vous pouvez également étendre le prototype Array:

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

Et maintenant, vous pouvez simplement utiliser les éléments suivants:

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

indexOf peut-être, mais c’est une extension "JavaScript" de la norme ECMA-262; en tant que tel, il peut ne pas être présent dans d'autres implémentations de la norme. "

Exemple:

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

AFAICS Microsoft ne n'offre pas d'alternative à cela, mais vous pouvez ajouter des fonctionnalités similaires aux tableaux dans Internet Explorer (et aux autres navigateurs ne prenant pas en charge indexOf ) si vous le souhaitez. , en tant que recherche rapide Google révèle (par exemple, celui-ci ).

ECMAScript 7 présente les Array. prototype.includes .

Il peut être utilisé comme ceci:

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

Il accepte également un deuxième argument facultatif fromIndex :

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

Contrairement à indexOf , qui utilise Comparaison stricte , inclut compare avec SameValueZero algorithme d'égalité. Cela signifie que vous pouvez détecter si un tableau inclut un NaN :

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

De plus, contrairement à indexOf , inclut ne ignore pas les index manquants:

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

Il s'agit actuellement d'un brouillon, mais il peut s'agir polyfilled pour le faire fonctionner sur tous les navigateurs.

b est la valeur et a est le tableau. Il retourne true ou false :

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

Les premières réponses supposent des types primitifs, mais si vous voulez savoir si un tableau contient un objet avec un trait, Array.prototype.some () est une solution très élégante:

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

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

La bonne chose à ce sujet est que l'itération est abandonnée une fois que l'élément est trouvé, donc les cycles d'itération inutiles sont sauvegardés.

De plus, il s’intègre parfaitement dans une instruction si car il renvoie un booléen:

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

* Comme jamess l'a souligné dans le commentaire, à compter d'aujourd'hui, septembre 2018, Array.prototype.some () est entièrement pris en charge: Tableau de support caniuse.com

Voici une implémentation JavaScript 1.6 compatible du code < > 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;
        };
}

Utiliser:

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

// Usage
if(isInArray(my_array, "my_value"))
{
    //...
}
L'extension de l'objet Array dans JavaScript est une très mauvaise idée, car vous introduisez de nouvelles propriétés (vos méthodes personnalisées) dans des boucles for-in qui peuvent altérer les scripts existants. Il y a quelques années, les auteurs de la bibliothèque Prototype ont dû reconfigurer leur implémentation de bibliothèque pour supprimer uniquement ce genre de chose.

Si vous n'avez pas à vous soucier de la compatibilité avec les autres scripts JavaScript en cours d'exécution sur votre page, n'hésitez pas, sinon, je vous recommanderais la solution plus autonome, plus délicate mais plus sûre.

Réfléchissez un instant, si vous passez cet appel plusieurs fois, il est beaucoup plus efficace d’utiliser un tableau associatif une carte pour effectuer des recherches à l’aide d’une fonction de hachage.

https://developer.mozilla.org/ en-US / docs / Web / JavaScript / Référence / Global_Objects / Carte

One-Liner:

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

J'utilise les éléments suivants:

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 () a été ajouté à la norme ECMA-262 dans la 5ème édition

Un indexOf / lastIndexOf alternatif bidirectionnel plus rapide, plus

2015

Alors que la nouvelle méthode inclut est très agréable, le support est pratiquement nul pour le moment.

Cela faisait longtemps que je cherchais un moyen de remplacer les fonctions lentes indexOf / lastIndexOf.

Un moyen performant a déjà été trouvé, en regardant les principales réponses. Parmi ceux que j'ai choisis, la fonction contient publiée par @Damir Zekic, qui devrait être la plus rapide. Mais il est également précisé que les repères datent de 2008 et sont donc dépassés.

Je préfère aussi tant que plutôt que pour , mais pour une raison non spécifique, j'ai fini d'écrire la fonction avec une boucle for. Cela pourrait également être fait avec un while - .

J'étais curieux de savoir si l'itération était beaucoup plus lente si je vérifiais les deux côtés du tableau en le faisant. Apparemment non, et cette fonction est donc environ deux fois plus rapide que les plus votées. Évidemment, c'est aussi plus rapide que le natif. Ceci dans un environnement réel, où vous ne savez jamais si la valeur que vous recherchez se trouve au début ou à la fin du tableau.

Lorsque vous savez que vous venez de placer un tableau avec une valeur, lastIndexOf reste probablement la meilleure solution, mais si vous devez parcourir de grands tableaux et que le résultat risque d'être omniprésent, il peut s'agir d'une solution solide pour accélérer les choses.

indexOf bidirectionnel / 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');

Test de performance

http://jsperf.com/bidirectionalindexof

Comme test, j'ai créé un tableau avec 100 000 entrées.

Trois requêtes: au début, au milieu & amp; à la fin du tableau.

J'espère que vous trouvez également cela intéressant et testez les performances.

Remarque: comme vous pouvez le constater, j'ai légèrement modifié la fonction contient pour refléter l'indexOf & amp; lastIndexOf (donc true avec index et false avec -1 ). Cela ne devrait pas nuire.

La variante du prototype de groupe

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

La fonction peut également être facilement modifiée pour renvoyer true ou false, voire l'objet, la chaîne ou quoi que ce soit d'autre.

Et voici la variante 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');

Comment est-ce possible?

Je pense que le calcul simple pour obtenir l'index reflété dans un tableau est si simple qu'il est deux fois plus rapide qu'une itération de boucle réelle.

Voici un exemple complexe effectuant trois vérifications par itération, mais cela n’est possible qu’avec un calcul plus long qui provoque le ralentissement du code.

http://jsperf.com/bidirectionalindexof/2

Si vous utilisez JavaScript 1.6 ou une version ultérieure (Firefox 1.5 ou une version ultérieure), vous pouvez utiliser Array.indexOf . Sinon, je pense que vous allez vous retrouver avec quelque chose de similaire à votre code d'origine.

Si vous vérifiez de manière répétée l'existence d'un objet dans un tableau, vous devriez peut-être regarder dans

  1. Garder le tableau trié à tout moment en effectuant un tri par insertion dans votre tableau (placez de nouveaux objets au bon endroit)
  2. Créer des objets de mise à jour en tant qu'opération de suppression + tri et
  3. Utilisez une recherche binaire dans votre contient (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;
} 

Retourne l'index du tableau s'il est trouvé ou -1 s'il n'est pas trouvé

Nous utilisons cet extrait (fonctionne avec des objets, des tableaux, des chaînes):

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

Utilisation:

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

Utilisez la fonction de lodash.

Il est concis, précis et prend en charge plusieurs plates-formes.

La réponse acceptée ne répond même pas aux exigences.

Configuration requise: Recommandez le moyen le plus concis et le plus efficace de déterminer si un tableau JavaScript contient un objet.

Réponse acceptée:

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

Ma recommandation:

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

Notes:

$ .inArray fonctionne bien pour déterminer si une valeur scalaire existe dans un tableau de scalaires ...

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

... mais la question demande clairement un moyen efficace de déterminer si un objet est contenu dans un tableau.

Pour gérer à la fois les scalaires et les objets, vous pouvez procéder comme suit:

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

Solution qui fonctionne dans tous les navigateurs modernes:

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

Utilisation:

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

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

Utilisation:

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

Pourquoi utiliser JSON.stringify ?

Array.indexOf et Array.includes (ainsi que la plupart des réponses fournies ici) comparent uniquement par référence et non par valeur.

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

Bonus

One-liner ES6 non optimisé:

<*>

Remarque: La comparaison d'objets par valeur fonctionnera mieux si les clés sont dans le même ordre. Par mesure de sécurité, vous pouvez donc trier les clés d'abord avec un package tel que celui-ci: https://www.npmjs.com/package/sort-keys

Mise à jour de la fonction contient avec une optimisation perf. Merci à itinance de l'avoir signalé.

Bien que array.indexOf (x)! = - 1 soit le moyen le plus concis de le faire (et a été pris en charge par les navigateurs non Internet Explorer, pendant plus de dix ans ...), il n'est pas O (1), mais plutôt O (N), ce qui est terrible. Si votre tableau ne changera pas, vous pouvez convertir votre tableau en table de hachage, puis faites table [x]! == undefined ou === undefined :

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

Démo:

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

(Malheureusement, bien que vous puissiez créer un Array.prototype.contains pour "geler" un tableau et stocker une table de hachage dans this._cache sur deux lignes, cela donnerait des résultats erronés si vous choisissez de modifier votre tableau ultérieurement. JavaScript crochets insuffisants pour vous permettre de conserver cet état, contrairement à Python par exemple.)

ECMAScript 6 a une proposition élégante sur find.

  

La méthode find exécute la fonction de rappel une fois pour chaque élément.   présent dans le tableau jusqu'à ce qu'il en trouve un où le rappel renvoie un vrai   valeur. Si un tel élément est trouvé, find renvoie immédiatement la valeur   de cet élément. Sinon, find renvoie undefined. le rappel est   appelé uniquement pour les index du tableau ayant des valeurs assignées; il   n'est pas appelé pour les index qui ont été supprimés ou qui n'ont jamais   des valeurs attribuées.

Voici le documentation MDN sur cela.

La fonctionnalité de recherche fonctionne comme ceci.

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

Vous pouvez l'utiliser dans ECMAScript 5 et versions inférieures par définition de la fonction .

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

Utiliser:

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

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

démo

Pour savoir exactement ce que le tilde ~ fait à ce stade, reportez-vous à cette question Que fait un tilde lorsqu'il précède une expression? .

Utiliser:

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

Je sais que ce n’est pas la meilleure façon de faire, mais comme il n’existe pas de moyen IComparable natif d’interagir entre les objets, je suppose que c’est aussi proche que possible de comparer deux entités dans un tableau. Également, étendre un objet Array peut ne pas être une bonne chose à faire, mais parfois ça va (si vous en êtes conscient et le compromis).

On peut utiliser Définir avec le méthode "a ()":

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

Vous pouvez également utiliser cette astuce:

var arrayContains = function(object) {
  return (serverList.filter(function(currentObject) {
    if (currentObject === object) {
      return currentObject
    }
    else {
      return false;
    }
  }).length > 0) ? true : false
}
  1. Utilisez Array.indexOf (Object) .
  2. Avec ECMA 7, vous pouvez utiliser le Array.includes (Object) .
  3. Avec ECMA 6, vous pouvez utiliser Array.find (Nom de la fonction) Nom de la fonction est un utilisateur. fonction définie pour rechercher l'objet dans le tableau.

    J'espère que cela aide!

Chose similaire: trouve le premier élément par un "recherche lambda":

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

Utilisation:

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

Identique dans coffeescript:

Array.prototype.find = (search_lambda) -> @[@map(search_lambda).indexOf(true)]
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top