Comment vérifier si un tableau inclut un objet en JavaScript?
-
04-07-2019 - |
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
.
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:
- Boîte à outils Dojo:
dojo.indexOf (tableau, valeur, [fromIndex, findLast ])
- Prototype:
array.indexOf (valeur)
- MooTools:
array.indexOf (valeur)
- MochiKit:
findValue (tableau, valeur )
- MS Ajax:
array.indexOf (valeur)
- Poste:
Tableau ext. .contains (tableau, valeur)
- Lodash:
_. includes (tableau, valeur, [from])
(est-ce que_. contient
avant la version 4.0.0) - ECMAScript 2016: tableau
. includes (valeur)
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"))
{
//...
}
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.
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
- Garder le tableau trié à tout moment en effectuant un tri par insertion dans votre tableau (placez de nouveaux objets au bon endroit)
- Créer des objets de mise à jour en tant qu'opération de suppression + tri et
- 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
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
}
- Utilisez
Array.indexOf (Object)
. - Avec ECMA 7, vous pouvez utiliser le
Array.includes (Object)
. -
Avec ECMA 6, vous pouvez utiliser
Array.find (Nom de la fonction)
où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)]