Quelle est la manière la plus concise et la plus efficace de savoir si un tableau JavaScript contient une valeur?

C'est la seule façon que je connais de 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 meilleur et plus concis pour y parvenir?

Ceci est très étroitement lié à la question de débordement de pile Le meilleur moyen de trouver un élément dans un tableau JavaScript? qui traite de la recherche d'objets dans un tableau à l'aide de indexOf.

3932
brad 26 oct. 2008 à 02:14

27 réponses

Meilleure réponse

Les navigateurs modernes ont Array#includes, qui fait exactement cela et est largement pris en charge par tout le monde sauf IE:

console.log(['joe', 'jane', 'mary'].includes('jane')); //true

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


De nombreux frameworks proposent également des méthodes similaires:

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

4305
33 revs, 19 users 29% 11 févr. 2020 à 19:13

Utilisez la fonction certains de lodash.

Il est concis, précis et offre un excellent support multiplateforme.

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

Exigences: Recommandez le moyen le plus concis et le plus efficace pour savoir 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

Remarques:

$ .inArray fonctionne très 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)
11
l3x 21 oct. 2015 à 11:58

Utilisation:

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

// Usage
if(isInArray(my_array, "my_value"))
{
    //...
}
55
Peter Mortensen 7 janv. 2017 à 11:29

Les meilleures 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é, ce qui évite les cycles d'itération inutiles.

En outre, il s'intègre parfaitement dans une instruction if car il renvoie un booléen:

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

* Comme jamess l'a souligné dans le commentaire, au moment de cette réponse, septembre 2018, Array.prototype.some() est entièrement pris en charge: table de support caniuse.com

121
Michael 5 sept. 2019 à 11:17

ECMAScript 7 présente 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 d'égalité, includes compare en utilisant SameValueZero algorithme d'égalité. Cela signifie que vous pouvez détecter si un tableau comprend un NaN:

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

Contrairement à indexOf, includes n'ignore pas les indices manquants:

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

Actuellement, il s'agit toujours d'un brouillon, mais il peut être polyfilled pour le faire fonctionner sur tous les navigateurs.

166
Oriol 8 févr. 2016 à 16:53

indexOf peut-être, mais c'est une "extension JavaScript" à la norme ECMA-262; en tant que tel, il peut ne pas être présent dans d'autres mises en œuvre 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 pas offre une sorte d'alternative à cela, mais vous pouvez ajouter des fonctionnalités similaires aux tableaux dans Internet Explorer (et aux autres navigateurs qui ne prennent pas en charge indexOf) si vous le souhaitez, en tant que une recherche rapide sur Google révèle (par exemple, celui-ci).

201
Peter Mortensen 11 août 2011 à 23:41

Disons que vous avez défini un tableau comme ceci:

const array = [1, 2, 3, 4]

Vous trouverez ci-dessous trois façons de vérifier s'il y a un 3 dedans. Tous retournent soit true soit false.

Méthode Native Array (depuis ES2016) (table de compatibilité)

array.includes(3) // true

Comme méthode de tableau personnalisé (avant ES2016)

// Prefixing the method with '_' to avoid name clashes
Object.defineProperty(Array.prototype, '_includes', { value: function (v) { return this.indexOf(v) !== -1 }})
array._includes(3) // true

Fonction simple

const includes = (a, v) => a.indexOf(v) !== -1
includes(array, 3) // true
111
Gust van de Wal 13 nov. 2019 à 17:09

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

Usage:

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

Usage:

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

Pourquoi utiliser JSON.stringify?

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

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

Prime

Doublure ES6 non optimisée:

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

Remarque: La comparaison des objets par valeur fonctionnera mieux si les clés sont dans le même ordre, donc pour être sûr, vous pouvez d'abord trier les clés avec un package comme celui-ci: https://www.npmjs.com/package/sort-keys


Mise à jour de la fonction contains avec une optimisation des performances. Merci itinance de l'avoir signalé.

13
Igor Barbashin 4 juil. 2017 à 23:34

Utilisation:

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

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

Démo

Pour savoir exactement ce que font les tilde ~ à ce stade, reportez-vous à cette question Que fait un tilde quand il precede une expression? .

8
Community 23 mai 2017 à 12:03

Voici une compatible avec JavaScript 1.6 de 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;
        };
}
77
Narendra Jadhav 30 janv. 2019 à 10:23

On peut utiliser un Set qui a la 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'));
8
J D 24 janv. 2020 à 12:16
function inArray(elem,array)
{
    var len = array.length;
    for(var i = 0 ; i < len;i++)
    {
        if(array[i] == elem){return i;}
    }
    return -1;
} 

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

15
mpromonet 6 sept. 2014 à 16:22
function contains(a, obj) {
    return a.some(function(element){return element == obj;})
}

Array.prototype.some () a été ajouté à l'ECMA -262 standard dans la 5e édition

23
dansalmo 12 sept. 2014 à 16:55

Si vous utilisez JavaScript 1.6 ou version ultérieure (Firefox 1.5 ou 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.

14
Andru Luvisi 25 oct. 2008 à 22:44

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
15
dr.dimitru 10 sept. 2014 à 12:12

ECMAScript 6 a une proposition élégante à trouver.

La méthode find exécute la fonction de rappel une fois pour chaque élément présent dans le tableau jusqu'à ce qu'elle en trouve un où le rappel renvoie une valeur vraie. Si un tel élément est trouvé, find renvoie immédiatement la valeur de cet élément. Sinon, find renvoie undefined. le rappel n'est invoqué que pour les index du tableau auxquels des valeurs ont été attribuées; elle n'est pas invoquée pour les index qui ont été supprimés ou auxquels aucune valeur n'a été affectée.

Voici la documentation MDN à ce sujet .

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 utiliser ceci dans ECMAScript 5 et ci-dessous par définir 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;
    }
  });
}
9
Peter Mortensen 7 janv. 2017 à 11:27

Si vous vérifiez à plusieurs reprises l'existence d'un objet dans un tableau, vous devriez peut-être

  1. Garder le tableau trié à tout moment en effectuant le tri par insertion dans votre tableau (placez de nouveaux objets dans le bon endroit)
  2. Faire la mise à jour des objets comme une opération de suppression et d'insertion triée et
  3. Utilisez une recherche binaire dans votre contains(a, obj).
14
Ztyx 5 févr. 2011 à 18:02

OK, vous pouvez simplement optimiser votre code pour obtenir le résultat!

Il existe de nombreuses façons de le faire qui sont plus propres et meilleures, mais je voulais juste obtenir votre modèle et l'appliquer en utilisant JSON.stringify, faites simplement quelque chose comme ceci dans votre cas:

function contains(a, obj) {
    for (var i = 0; i < a.length; i++) {
        if (JSON.stringify(a[i]) === JSON.stringify(obj)) {
            return true;
        }
    }
    return false;
}
6
Alireza 6 févr. 2019 à 14:48

Alors que array.indexOf(x)!=-1 est le moyen le plus concis de le faire (et est pris en charge par les navigateurs non Internet Explorer depuis plus de dix ans ...), ce n'est pas O (1), mais plutôt O (N), qui est terrible. Si votre tableau ne change 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 tableau.prototype.contains pour «figer» 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 plus tard. JavaScript n'a pas suffisamment de crochets pour vous permet de conserver cet état, contrairement à Python par exemple.)

9
Peter Mortensen 7 janv. 2017 à 11:30

L'extension de l'objet JavaScript Array 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 casser les scripts existants. Il y a quelques années, les auteurs de la bibliothèque Prototype ont dû repenser leur implémentation de bibliothèque pour supprimer simplement ce genre de chose.

Si vous n'avez pas à vous soucier de la compatibilité avec d'autres JavaScript exécutés sur votre page, allez-y, sinon, je recommanderais la solution de fonction autonome plus maladroite mais plus sûre.

48
Peter Mortensen 11 août 2011 à 23:43

Mise à jour de 2019: cette réponse date de 2008 (11 ans!) et n'est pas pertinente pour une utilisation JS moderne. L'amélioration des performances promise était basée sur une référence réalisée dans les navigateurs de l'époque. Il peut ne pas être pertinent pour les contextes d'exécution JS modernes. Si vous avez besoin d'une solution simple, cherchez d'autres réponses. Si vous avez besoin des meilleures performances, comparez vous-même dans les environnements d'exécution appropriés.

Comme d'autres l'ont dit, l'itération à travers le tableau est probablement le meilleur moyen, mais elle a été prouvée qu'une boucle while décroissante est le moyen le plus rapide d'itérer en JavaScript. Vous voudrez donc 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 ce qui suit:

alert([1, 2, 3].contains(2)); // => true
alert([1, 2, 3].contains('2')); // => false
428
Damir Zekić 7 août 2019 à 10:49

Une solution simple pour cette exigence consiste à utiliser find()

Si vous rencontrez un tableau d'objets comme ci-dessous,

var users = [{id: "101", name: "Choose one..."},
{id: "102", name: "shilpa"},
{id: "103", name: "anita"},
{id: "104", name: "admin"},
{id: "105", name: "user"}];

Ensuite, vous pouvez vérifier si l'objet avec votre valeur est déjà présent ou non

let data = users.find(object => object['id'] === '104');

Si les données sont nulles, aucun administrateur, sinon, il retournera l'objet existant comme ci-dessous.

{id: "104", name: "admin"}

Ensuite, vous pouvez trouver l'index de cet objet dans le tableau et remplacer l'objet à l'aide du code ci-dessous.

let indexToUpdate = users.indexOf(data);
let newObject = {id: "104", name: "customer"};
users[indexToUpdate] = newObject;//your new object
console.log(users);

Vous obtiendrez de la valeur comme ci-dessous

[{id: "101", name: "Choose one..."},
{id: "102", name: "shilpa"},
{id: "103", name: "anita"},
{id: "104", name: "customer"},
{id: "105", name: "user"}];

J'espère que cela aidera n'importe qui.

5
Shiva 11 oct. 2019 à 17:38
    function countArray(originalArray) {
     
    	var compressed = [];
    	// make a copy of the input array
    	var copyArray = originalArray.slice(0);
     
    	// first loop goes over every element
    	for (var i = 0; i < originalArray.length; i++) {
     
    		var count = 0;	
    		// loop over every element in the copy and see if it's the same
    		for (var w = 0; w < copyArray.length; w++) {
    			if (originalArray[i] == copyArray[w]) {
    				// increase amount of times duplicate is found
    				count++;
    				// sets item to undefined
    				delete copyArray[w];
    			}
    		}
     
    		if (count > 0) {
    			var a = new Object();
    			a.value = originalArray[i];
    			a.count = count;
    			compressed.push(a);
    		}
    	}
     
    	return compressed;
    };
    
    // It should go something like this:
    
    var testArray = new Array("dog", "dog", "cat", "buffalo", "wolf", "cat", "tiger", "cat");
    var newArray = countArray(testArray);
    console.log(newArray);
5
J D 24 janv. 2020 à 12:01

Performance

Aujourd'hui 2020.01.07 j'effectue des tests sur MacOs HighSierra 10.13.6 sur Chrome v78.0.0, Safari v13.0.4 et Firefox v71.0.0 pour 15 solutions choisies. Conclusions

  • les solutions basées sur JSON, Set et étonnamment find (K, N, O) sont les plus lentes sur tous les navigateurs
  • l'es6 includes (F) est rapide uniquement sur chrome
  • les solutions basées sur for (C, D) et indexOf (G, H) sont assez rapides sur tous les navigateurs sur les petites et grandes baies, elles sont donc probablement le meilleur choix pour une solution efficace
  • les solutions où l'index diminue pendant la boucle, (B) est plus lente probablement parce que le chemin de Le cache du processeur fonctionne.
  • Je lance également un test pour un grand tableau lorsque l'élément recherché était en position 66% de la longueur du tableau, et les solutions basées sur for (C, D, E) donnent des résultats similaires (~ 630 ops / sec - mais le E sur safari et firefox était 10-20% plus lent que C et D)

Résultats

enter image description here

Détails

J'exécute 2 cas de tests: pour tableau avec 10 éléments, et tableau avec 1 million d'éléments. Dans les deux cas, nous mettons l'élément recherché au milieu du tableau.

let log = (name,f) => console.log(`${name}: 3-${f(arr,'s10')}  's7'-${f(arr,'s7')}  6-${f(arr,6)} 's3'-${f(arr,'s3')}`)

let arr = [1,2,3,4,5,'s6','s7','s8','s9','s10'];
//arr = new Array(1000000).fill(123); arr[500000]=7;

function A(a, val) {
    var i = -1;
    var n = a.length;
    while (i++<n) {
       if (a[i] === val) {
           return true;
       }
    }
    return false;
}

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

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

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

function E(a, val){  
  var n = a.length-1;
  var t = n/2;
  for (var i = 0; i <= t; i++) {
        if (a[i] === val || a[n-i] === val) return true;
  }
  return false;
}

function F(a,val) {
	return a.includes(val);
}

function G(a,val) {
	return a.indexOf(val)>=0;
}

function H(a,val) {
	return !!~a.indexOf(val);
}

function I(a, val) {
  return a.findIndex(x=> x==val)>=0;
}

function J(a,val) {
	return a.some(x=> x===val);
}

function K(a, val) {
  const s = JSON.stringify(val);
  return a.some(x => JSON.stringify(x) === s);
}

function L(a,val) {
	return !a.every(x=> x!==val);
}

function M(a, val) {
  return !!a.find(x=> x==val);
}

function N(a,val) {
	return a.filter(x=>x===val).length > 0;
}

function O(a, val) {
  return new Set(a).has(val);
}

log('A',A);
log('B',B);
log('C',C);
log('D',D);
log('E',E);
log('F',F);
log('G',G);
log('H',H);
log('I',I);
log('J',J);
log('K',K);
log('L',L);
log('M',M);
log('N',N);
log('O',O);
This shippet only presents functions used in performance tests - it not perform tests itself!

Tableau petit - 10 éléments

Vous pouvez effectuer des tests sur votre machine ICI

enter image description here

Grand tableau - 1 000 000 d'éléments

Vous pouvez effectuer des tests sur votre machine ICI

enter image description here

11
Kamil Kiełczewski 11 mars 2020 à 16:26

Bon mot:

function contains(arr, x) {
    return arr.filter(function(elem) { return elem == x }).length > 0;
}
31
Peter Mortensen 7 janv. 2017 à 11:25

J'utilise ce qui suit:

Array.prototype.contains = function (v) {
    return this.indexOf(v) > -1;
}

var a = [ 'foo', 'bar' ];

a.contains('foo'); // true
a.contains('fox'); // false
26
Eduardo Cuomo 15 juin 2014 à 01:15
237104