Comment puis-je vérifier si un tableau inclut un objet en JavaScript?
Quelle est la manière la plus concise et 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;
}
y a-t-il une façon meilleure et plus concise d'y parvenir?
Ceci est très étroitement lié à la question de débordement de la pile meilleure façon de trouver un article dans un tableau JavaScript? qui traite de la recherche d'objets en un tableau utilisant indexOf
.
30 réponses
les navigateurs actuels ont Array#includes
, qui fait exactement que, est largement supporté , et a un polyfill pour les navigateurs plus anciens.
> ['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 périmés.
jQuery offre $.inArray
, qui est fonctionnellement équivalent à Array#indexOf
.
trait de soulignement.js , une bibliothèque utilitaire JavaScript, offre _.contains(list, value)
, alias _.include(list, value)
, qui utilisent tous les deux indexOf en interne si passé un tableau JavaScript.
certains autres cadres offrent des méthodes similaires:
- Dojo Toolkit:
dojo.indexOf(array, value, [fromIndex, findLast])
- Prototype:
array.indexOf(value)
- MooTools:
array.indexOf(value)
- MochiKit:
findValue(array, value)
- MS Ajax:
array.indexOf(value)
- Ext:
Ext.Array.contains(array, value)
- Lodash:
_.includes(array, value, [from])
(_.contains
avant 4.0.0) - ECMAScript 2016:
array.includes(value)
remarquez que certains frameworks implémentent ceci comme une fonction, tandis que d'autres ajoutent la fonction au prototype du tableau.
mise à jour: comme @orip le mentionne dans les commentaires, Le benchmark lié a été fait en 2008, de sorte que les résultats peuvent ne pas être pertinents pour les navigateurs modernes. Cependant, vous avez probablement besoin de cela pour prendre en charge les navigateurs non-modernes de toute façon et ils n'ont probablement pas été mis à jour depuis. Toujours tester par vous-même.
comme d'autres l'ont dit, l'itération à travers le tableau est probablement la meilleure façon, mais il a été prouvé qu'un décroissant while
boucle est le moyen le plus rapide d'itérer en JavaScript. Vous pouvez donc 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 aussi bien étendre prototype de réseau:
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
indexOf
peut-être, mais C'est une extension JavaScript à la norme ECMA-262; en tant que telle, elle peut ne pas être présente 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 pas une sorte d'alternative pour cela, mais vous pouvez ajouter des fonctionnalités similaires aux tableaux dans Internet Explorer (et les autres navigateurs qui ne sont pas soutenez indexOf
) si vous voulez, comme un la recherche rapide de Google révèle (par exemple, celui-ci ).
ECMAScript 7 introduit 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 second argument optionnel fromIndex
:
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
Contrairement à indexOf
, qui utilise Égalité Stricte Comparaison , includes
compare à l'aide de SameValueZero l'égalité de l'algorithme. Cela signifie que vous pouvez détecter si un tableau inclut un NaN
:
[1, 2, NaN].includes(NaN); // true
également à la différence de indexOf
, includes
ne saute pas les indices manquants:
new Array(5).includes(undefined); // true
Actuellement, il est encore un projet, mais peut être 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
}
voici un JavaScript 1.6 compatible mise en œuvre 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;
};
}
utiliser:
function isInArray(array, search)
{
return array.indexOf(search) >= 0;
}
// Usage
if(isInArray(my_array, "my_value"))
{
//...
}
les réponses du haut supposent des types primitifs mais si vous voulez savoir si un tableau contient un objet avec un trait, tableau.prototype.certains () 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, c'est que l'itération est annulée une fois que l'élément est trouvé, donc inutile d'itération cycles sont enregistrés.
aussi, il s'inscrit bien dans un if
déclaration 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 soutenu: caniuse.com tableau de soutien
étendre L'objet JavaScript Array
est une très mauvaise idée parce que vous introduisez de nouvelles propriétés (vos méthodes personnalisées) dans des boucles for-in
qui peuvent briser les scripts existants. Il y a quelques années, les auteurs de la Prototype bibliothèque a dû repenser leur mise en œuvre de la bibliothèque pour supprimer justement ce genre de chose.
si vous n'avez pas à vous soucier de la compatibilité avec D'autres JavaScript tournant sur votre page, allez-y, sinon, je recommanderais la solution de fonction autonome plus embarrassante, mais plus sûre.
penser hors de la boîte pour une seconde, si vous faites cet appel plusieurs fois, il est beaucoup plus efficace d'utiliser un tableau associatif une carte pour faire des recherches en utilisant une fonction de hachage.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
One-liner:
function contains(arr, x) {
return arr.filter(function(elem) { return elem == x }).length > 0;
}
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
function contains(a, obj) {
return a.some(function(element){return element == obj;})
}
"151920920 Tableau.prototype.certains () a été ajouté à la norme ECMA-262 dans la 5e édition
a hopeferately faster bidirectional indexOf
/ lastIndexOf
alternative
2015
alors que la nouvelle méthode comprend est très agréable, le support est essentiellement zéro pour l'instant.
il y a longtemps que je pensais à une façon de remplacer le lent indexOf/latindexof fonctions.
une voie performante a déjà été trouvée, en regardant le top des réponses. Parmi ceux-ci, j'ai choisi la fonction contains
Postée par @Damir Zekic qui devrait être la plus rapide. Mais il indique également que les critères de référence sont de 2008 et sont donc dépassés.
je préfère aussi while
plutôt que for
, mais pour une raison non spécifique j'ai terminé l'écriture de la fonction avec une boucle for. On pourrait aussi le faire avec un while --
.
j'étais curieux si l'itération était beaucoup plus lente si je vérifie les deux côtés du tableau tout en le faisant. Apparemment non, et donc cette fonction est environ deux fois plus rapide que les plus votées. Évidemment, il est aussi plus rapide que le natif. Ce dans un environnement réel, où vous ne savez jamais si la valeur recherchée est au début ou à la fin du tableau.
quand vous savez que vous venez de pousser un tableau avec une valeur, en utilisant latindexof reste probablement la meilleure solution, mais si vous devez voyager à travers de grands tableaux et le résultat pourrait être partout, cela pourrait être une solution solide pour rendre les choses plus vite.
index bidirectionnel de/dernier index de
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');
essai de Performance
http://jsperf.com/bidirectionalindexof
comme test j'ai créé un tableau avec des entrées de 100k.
Trois requêtes: au début, au milieu et à la fin du tableau.
j'espère que vous trouverez aussi cela intéressant et tester la performance.
Note: Comme vous pouvez le voir j'ai légèrement modifié la fonction contains
pour refléter l'indexOf & latindexof sortie (donc essentiellement true
avec le index
et false
avec -1
). Qui ne devrait pas lui nuire.
Le tableau prototype 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');
La fonction peut également être facilement modifié pour retournez true ou false ou même 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 simple calcul pour obtenir l'indice réfléchi dans un tableau est si simple qu'il est deux fois plus rapide que de faire une itération de boucle réelle.
voici un exemple complexe faisant trois contrôles par itération, mais cela est seulement possible avec plus de calculs qui provoque le ralentissement du code.
si vous vérifiez à plusieurs reprises l'existence d'un objet dans un tableau, vous devriez peut-être regarder dans
- garder le tableau trié en tout temps en faisant insertion tri dans votre tableau (mettez les nouveaux objets à la bonne place)
- Make updating objects as remove+tried insert operation et
- Utiliser un binaire de recherche recherche dans votre
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;
}
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, tableaux, cordes):
/*
* @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
si vous utilisez JavaScript 1.6 ou plus tard (Firefox 1.5 ou plus tard), vous pouvez utiliser le tableau .index de . Sinon, je pense que vous allez finir avec quelque chose de similaire à votre code d'origine.
utilisez la fonction de lodash.
il est concis, précis et a un grand soutien de plateforme croisée.
la réponse acceptée ne répond même pas aux exigences.
: Recommander plus concise et 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
Notes:
$.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 pour déterminer si un objet est contenu dans un tableau.
pour manipuler à la fois des scalaires et des objets, vous pouvez faire ceci:
(_.isObject(item)) ? _.some(ary, item) : (_.indexOf(ary, item) > -1)
alors que array.indexOf(x)!=-1
est la façon la plus concise de le faire (et a été pris en charge par les navigateurs Non-Internet Explorer depuis plus de dix ans...), ce n'est pas O(1), mais O(N), qui est terrible. Si votre tableau ne sera pas changer, vous pouvez convertir votre tableau en un hashtable, puis faire 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, alors que vous pouvez créer un Tableau.prototype.contient de "geler" un tableau et stocker une table de hachage._cache en deux lignes, cela donnerait de mauvais résultats si vous choisissez d'éditer votre tableau plus tard. JavaScript a des crochets insuffisants pour vous permettre de garder cet état, contrairement à Python par exemple.)
ECMAScript 6 a une élégante proposition sur find.
la méthode find exécute la fonction callback une fois pour chaque élément présent dans le tableau jusqu'à ce qu'il en trouve un où callback renvoie un true valeur. Si un tel élément est trouvé, trouver renvoie immédiatement la valeur de cet élément. Sinon, les retours de find ne sont pas définis. le rappel est invoqué seulement pour les index du tableau qui ont assigné des valeurs; il n'est pas invoquée pour les indices qui ont été supprimé ou qui n'ont jamais été affectées des valeurs.
Voici la "documentation MDN à ce sujet.
la fonctionnalité find 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 ci-dessous par définissant 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;
}
});
}
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
IE6+ solution:
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:
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) comparer uniquement par référence et non par valeur.
[{a: 1}, {a: 2}].includes({a: 1});
// false, because {a: 1} is a new object
Bonus
one-liner ES6 Non optimisé:
[{a: 1}, {a: 2}].some(item => JSON.stringify(item) === JSON.stringify({a: 1));
// true
Note: Comparer des objets en valeur fonctionnera mieux si les clés sont dans le même ordre, donc pour être sûr vous pourriez trier les clés en premier avec un paquet comme celui-ci: https://www.npmjs.com/package/sort-keys
mise à Jour contains
fonction avec une perf d'optimisation. Merci itinance pour l'avoir montré.
utiliser:
var myArray = ['yellow', 'orange', 'red'] ;
alert(!!~myArray.indexOf('red')); //true
pour savoir exactement ce que fait le tilde
~
à ce point, référez-vous à cette question Que fait un tilde lorsqu'il précède une expression? .
Voici comment Prototype n'est :
/**
* 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 — 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;
}
Voir aussi ici pour la façon dont ils l'accrochent.
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 c'est pas la meilleure façon d'aller, mais puisqu'il n'est pas natif IComparable façon d'interagir entre les objets, je suppose que c'est aussi proche que vous pouvez obtenir pour comparer deux entités dans un tableau. En outre, étendre objet Array n'est peut-être pas une chose sage à faire, mais parfois C'est OK (si vous êtes au courant de cela et du compromis).
vous pouvez également utiliser ce truc:
var arrayContains = function(object) {
return (serverList.filter(function(currentObject) {
if (currentObject === object) {
return currentObject
}
else {
return false;
}
}).length > 0) ? true : false
}
OK, vous pouvez juste optimiser votre code pour obtenir le résultat! Il y a de nombreuses façons de faire cela qui sont plus propres et mieux, mais je voulais juste obtenir votre modèle et s'appliquer à cela en utilisant JSON.stringify
, simplement faire quelque chose comme ça 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;
}
comme d'autres l'ont mentionné , vous pouvez utiliser Array.indexOf
, mais il n'est pas disponible dans tous les navigateurs. Voici le code à partir de https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf pour le faire fonctionner même dans les navigateurs plus anciens.
indexOf est un ajout récent à la norme ECMA-262; en tant que tel, il peut ne pas être présent dans tous les navigateurs. Vous pouvez contourner cela en insérant le code suivant à le début de vos scripts, permettant l'utilisation index des implémentations qui ne le supportent pas nativement. Ce l'algorithme est exactement celui spécifié dans ECMA-262, 5e édition, en présumant L'objet, le typographe, le nombre, Les Maths.étage, les Maths.abs, et les Mathématiques.Max ont leur valeur d'origine.
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;
}
}
ce n'est pas le meilleur, mais je devenais créatif et j'ajoutais au répertoire.
n' utilisez pas ce
Object.defineProperty(Array.prototype, 'exists', {
value: function(element, index) {
var index = index || 0
return index === this.length ? -1 : this[index] === element ? index : this.exists(element, ++index)
}
})
// Outputs 1
console.log(['one', 'two'].exists('two'));
// Outputs -1
console.log(['one', 'two'].exists('three'));
console.log(['one', 'two', 'three', 'four'].exists('four'));