Si j'ai un tableau [1, 2, 3, 5, 2, 8, 9, 2]
, je voudrais vérifier combien de 2
il y a dans le tableau. Quelle est la manière la plus élégante de le faire en JavaScript sans passer par la boucle for
?
[cette réponse date un peu : lisez les modifications].
Dites bonjour à vos amis : map
et filter
et reduce
et forEach
et every
etc.
(Je n'écris qu'occasionnellement des for-loops en javascript, parce que le scoping au niveau du bloc est manquant, donc vous devez utiliser une fonction comme corps de la boucle de toute façon si vous avez besoin de capturer ou cloner votre index ou valeur d'itération. Les for-loops sont plus efficaces en général, mais parfois vous avez besoin d'une fermeture).
La manière la plus lisible :
[....].filter(x => x==2).length
(Nous aurions pu écrire .filter(function(x){return x==2}).length
à la place)
La méthode suivante est plus efficace en termes d'espace (O(1) plutôt que O(N)), mais je ne suis pas sûr de l'avantage ou du désavantage en termes de temps (pas plus qu'un facteur constant puisque vous visitez chaque élément exactement une fois) :
[....].reduce((total,x) => (x==2 ? total+1 : total), 0)
(Si vous avez besoin d'optimiser ce morceau de code particulier, une boucle for pourrait être plus rapide sur certains navigateurs... vous pouvez tester les choses sur jsperf.com).
Vous pouvez ensuite être élégant et le transformer en une fonction prototype :
[1, 2, 3, 5, 2, 8, 9, 2].count(2)
Comme ceci :
Object.defineProperties(Array.prototype, {
count: {
value: function(value) {
return this.filter(x => x==value).length;
}
}
});
Vous pouvez également utiliser la bonne vieille technique de la boucle for (voir les autres réponses) à l'intérieur de la définition de la propriété ci-dessus (là encore, ce serait probablement beaucoup plus rapide).
2017 edit :
Oups, cette réponse est devenue plus populaire que la bonne réponse. En fait, il suffit d'utiliser la réponse acceptée. Bien que cette réponse puisse être mignonne, les compilateurs js n'optimisent probablement pas (ou ne peuvent pas en raison des spécifications) de tels cas. Donc vous devriez vraiment écrire une simple boucle for :
Object.defineProperties(Array.prototype, {
count: {
value: function(query) {
/*
Counts number of occurrences of query in array, an integer >= 0
Uses the javascript == notion of equality.
*/
var count = 0;
for(let i=0; i<this.length; i++)
if (this[i]==query)
count++;
return count;
}
}
});
Vous pourriez définir une version .countStrictEq(...)
qui utilise la notion d'égalité ===
. La notion d'égalité peut être importante pour ce que vous faites ! (par exemple [1,10,3,'10' ;].count(10)==2
, parce que des nombres comme '4'==4 en javascript... d'où le fait de l'appeler .countEq
ou .countNonstrict
souligne qu'il utilise l'opérateur ==
).
Envisagez également d'utiliser votre propre structure de données multi-ensembles (par exemple, comme les 'collections.Counter
' ; de Python) pour éviter d'avoir à faire le comptage en premier lieu.
class Multiset extends Map {
constructor(...args) {
super(...args);
}
add(elem) {
if (!this.has(elem))
this.set(elem, 1);
else
this.set(elem, this.get(elem)+1);
}
remove(elem) {
var count = this.has(elem) ? this.get(elem) : 0;
if (count>1) {
this.set(elem, count-1);
} else if (count==1) {
this.delete(elem);
} else if (count==0)
throw `tried to remove element ${elem} of type ${typeof elem} from Multiset, but does not exist in Multiset (count is 0 and cannot go negative)`;
// alternatively do nothing {}
}
}
Démonstration :
> counts = new Multiset([['a',1],['b',3]])
Map(2) {"a" => 1, "b" => 3}
> counts.add('c')
> counts
Map(3) {"a" => 1, "b" => 3, "c" => 1}
> counts.remove('a')
> counts
Map(2) {"b" => 3, "c" => 1}
> counts.remove('a')
Uncaught tried to remove element a of type string from Multiset, but does not exist in Multiset (count is 0 and cannot go negative)
sidenote : Si vous voulez toujours utiliser la méthode de programmation fonctionnelle (ou une ligne unique sans surcharger Array.prototype), vous pouvez l'écrire plus simplement aujourd'hui comme [...].filter(x => x==2).length
. Si vous vous préoccupez des performances, notez que bien que cette méthode ait asymptotiquement les mêmes performances que la boucle for (O(N) temps), elle peut nécessiter O(N) mémoire supplémentaire (au lieu de O(1) mémoire) car elle va presque certainement générer un tableau intermédiaire et ensuite compter les éléments de ce tableau intermédiaire.
Le moyen le plus bizarre auquel je pense pour faire ça est :
(a.length-(' '+a.join(' ')+' ').split(' '+n+' ').join(' ').match(/ /g).length)+1
Où :
Ma suggestion : utilisez une boucle while ou for ;-)