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
.
27 réponses
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:
- jQuery:
$.inArray(value, array, [fromIndex])
- Underscore.js:
_.contains(array, value)
(également alias_.include
et_.includes
) - Boîte à outils Dojo:
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])
(est_.contains
antérieur au 4.0.0) - Ramda:
R.includes(value, array)
Notez que certains frameworks implémentent cela en tant que fonction, tandis que d'autres ajoutent la fonction au prototype de tableau.
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)
Utilisation:
function isInArray(array, search)
{
return array.indexOf(search) >= 0;
}
// Usage
if(isInArray(my_array, "my_value"))
{
//...
}
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
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.
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 / em> 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).
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
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é.
Utilisation:
var myArray = ['yellow', 'orange', 'red'] ;
alert(!!~myArray.indexOf('red')); //true
Pour savoir exactement ce que font les tilde
~
à ce stade, reportez-vous à cette question Que fait un tilde quand il precede une expression? .
Voici comment Prototype
/**
* 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 également ici le brancher.
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;
};
}
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'));
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é
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
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.
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
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;
}
});
}
Si vous vérifiez à plusieurs reprises l'existence d'un objet dans un tableau, vous devriez peut-être
- Garder le tableau trié à tout moment en effectuant le tri par insertion dans votre tableau (placez de nouveaux objets dans le bon endroit)
- Faire la mise à jour des objets comme une opération de suppression et d'insertion triée et
- Utilisez une recherche binaire dans votre
contains(a, obj)
.
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;
}
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.)
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.
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
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.
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);
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 étonnammentfind
(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) etindexOf
(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
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
Grand tableau - 1 000 000 d'éléments
Vous pouvez effectuer des tests sur votre machine ICI
Bon mot:
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
Questions connexes
De nouvelles questions
javascript
Pour des questions concernant la programmation dans ECMAScript (JavaScript / JS) et ses divers dialectes / implémentations (hors ActionScript). Veuillez inclure toutes les balises pertinentes dans votre question; par exemple, [node.js], [jquery], [json], etc.