Maison >interface Web >js tutoriel >Compter les choses en Javascript
Si vous souhaitez compter le nombre d'éléments d'une variété spécifique dans un tableau, vous pouvez filtrer ce tableau et vérifier la longueur du résultat.
const letters = ['a','b','b','c','c','c']; const numberOfC = letters.filter(letter => letter === 'c').length; console.log(numberOfC); // 3
Cela a une complexité temporelle de O(n), qui est l'idéal théorique pour ce type de problème. Il utilise de la mémoire supplémentaire pour contenir un deuxième tableau, mais pour ne compter qu'une seule variété d'éléments, il s'agit d'une approche courante et suffisamment efficace dans la plupart des situations. Cependant, ce n'est pas une approche efficace si vous souhaitez compter toutes les variétés d'éléments du tableau.
Si nous voulions compter non seulement le « c » mais aussi chaque lettre du tableau, l'application naïve de cette technique ressemblerait à ceci :
const letters = ['a','b','b','c','c','c']; letters.forEach((letter, _, arr) => { const count = arr.filter(otherLetter => otherLetter === letter).length; console.log(letter, count); });
Cela produit le résultat suivant :
a 1 b 2 b 2 c 3 c 3 c 3
Les décomptes sont corrects, mais il y a une redondance, qui peut être corrigée en utilisant d'abord un Set.
const letters = ['a','b','b','c','c','c']; const uniqueLetters = new Set(letters); for(const letter of uniqueLetters){ const count = letters.filter(otherLetter => otherLetter === letter).length; console.log(letter, count); };
La sortie résultante n'a aucun doublon :
a 1 b 2 c 3
Cependant, il a une complexité temporelle de O(n^2), car chaque filtre parcourt l'ensemble du tableau, et il doit le faire pour chaque lettre unique.
Il est possible d'effectuer ce décompte avec une complexité temporelle O(n). Le principe général est de parcourir le tableau une seule fois. Pour chaque élément rencontré, vérifiez si vous disposez déjà d’un décompte pour cet élément. Si vous le faites, incrémentez-le de 1. Si vous ne le faites pas, ajoutez un nombre pour cet élément avec une valeur définie sur 1.
Javascript dispose de deux mécanismes différents adaptés au stockage des décomptes. Nous pouvons utiliser des objets ou des cartes. Notez qu'il s'agit d'une référence à la structure de données Map et non à la méthode map() des tableaux. Utiliser une carte est plus efficace, mais utiliser des objets pour cela est encore assez courant, nous verrons donc comment compter avec les deux.
const letters = ['a','b','b','c','c','c']; const counts = new Map(); // Creates an empty Map letters.forEach(letter => { if(counts.has(letter)) {//Determine if the letter already has a count counts.set(letter, counts.get(letter) + 1);//Increment existing count } else counts.set(letter, 1);//Set new count to 1 }); console.log(counts); //Map(3) { 'a' => 1, 'b' => 2, 'c' => 3 }
Le has() vérifie si la valeur est déjà dans la carte. Le set() stocke une valeur dans la carte, soit en créant une nouvelle valeur, soit en écrasant une valeur existante. Le get() obtient la valeur actuelle. Notez que has(), set() et get() ont une complexité temporelle O(1), car un hachage est utilisé en interne.
Si nous voulions extraire plus tard le nombre de lettres spécifiques, nous pourrions faire quelque chose comme :
console.log(counts.get('c'));//3
Bien que compter avec des objets soit plus lent, la différence de performances peut ne pas être perceptible si vous ne comptez pas une énorme quantité d'objets. Les objets sont également mieux pris en charge par le navigateur que Maps, bien que cette différence soit devenue presque sans conséquence à ce stade. Selon caniuse.com, Map est pris en charge dans 96,28 % des navigateurs au moment d'écrire ces lignes. Les objets font partie intégrante du langage Javascript et devraient être pris en charge à 100 % par le navigateur, mais caniuse.com n'a pas de liste pour cela. La principale raison pour apprendre à utiliser des objets pour les décomptes est qu’il existe de nombreux anciens codes utilisant cette technique. Certaines personnes qui ont appris de l'ancien code écrivent également du nouveau code en utilisant cette approche.
const letters = ['a','b','b','c','c','c']; const counts = {};//Create empty object for holding counts letters.forEach(letter => { if(letter in counts) {//Check if the count exists counts[letter]++;//Increment the count } else counts[letter] = 1;//Set new count to 1 }); console.log(counts);//{ a: 1, b: 2, c: 3 }
Ce code suit la même structure que le code utilisant une Map. Les différences résident dans la syntaxe des cartes et des objets. Le mot-clé "in" est utilisé pour tester si l'objet possède une clé portant ce nom. La syntaxe entre crochets est utilisée à la fois pour obtenir et définir les valeurs. Notez que tester l'appartenance, définir et obtenir des valeurs à partir d'un objet sont des opérations de complexité temporelle O(1), car les objets utilisent en interne un hachage pour les clés.
Si les choses que vous comptez sont des objets, des précautions supplémentaires sont nécessaires. Les cartes sont capables de stocker des objets sous forme de clés et préservent le type, mais cela crée un problème de comparaison d'égalité si vous avez un objet totalement différent qui a exactement les mêmes clés et valeurs.
const m = new Map(); m.set({name: "John"}, 5); console.log(m.has({name: "John"}));//false
Ce code renvoie "false", car les deux objets n'ont pas d'égalité référentielle. Les deux objets sont identiques en termes de clés et de valeurs, mais ce ne sont pas exactement le même objet.
const m = new Map(); const obj = {name: "John"} m.set(obj, 5); console.log(m.has(obj));//true
Cette version du code renvoie « vrai », car la valeur définie est exactement le même objet dont l'adhésion est testée.
Le résultat d'essayer de compter les objets avec Maps dans la plupart des cas est que tous les objets finiront avec un compte de 1, car chaque has() renvoie false.
L'utilisation d'objets pour compter des objets pose un problème connexe mais légèrement différent. Les clés d'objet sont automatiquement contraintes à la chaîne (Sauf lorsqu'il s'agit de symboles). Si vous essayez d'utiliser un objet comme clé d'un autre objet, ce type de coercition crée une clé égale à "[object Object]".
const obj = {name: "John"} const count = {}; count[obj] = 5; console.log(count);// { '[object Object]': 5 }
The result of trying to count objects using objects is you will end up with an object that has "[object Object]" as the key, and the value will be the total number of all the things that were counted. There won't be any individual counts, because every item is treated as "[object Object]".
It is not common that you would need to count objects. If the need arises, the solution depends on whether or not the objects have a unique property that is guaranteed to be unique. For example, objects that have an id number could be counted by counting the id number occurrences instead of counting occurrences of the whole object.
const employees = [ {id: 1, name: "John"}, {id: 1, name: "John"}, {id: 2, name: "Mary"}, {id: 2, name: "Mary"}, {id: 2, name: "Mary"}, ] const counts = new Map(); employees.forEach(employee => { if(counts.has(employee.id)) { counts.set(employee.id, counts.get(employee.id) + 1); } else counts.set(employee.id, 1); }); console.log(counts);//Map(2) { 1 => 2, 2 => 3 }
This only gives counts with ids and an extra lookup would be needed to associate the counts with names, but we can fix that with a bit more code:
const countsWithNames = []; for(const count of counts) { const employee = employees.find((employee) => employee.id === count[0]); countsWithNames.push({employee, count: count[1]}); } console.log(countsWithNames);
The resulting output is:
[ { employee: { id: 1, name: 'John' }, count: 2 }, { employee: { id: 2, name: 'Mary' }, count: 3 } ]
If there isn't anything like an id that guarantees uniqueness, the only remaining practical option would be to first convert the object to JSON.
const people = [ {name: "John Doe", age: 25}, {name: "John Doe", age: 25}, {name: "Mary Jane", age: 24}, {name: "Mary Jane", age: 24}, {name: "Mary Jane", age: 24} ] const counts = new Map(); people.forEach(person => { const personString = JSON.stringify(person); if(counts.has(personString)) { counts.set(personString, counts.get(personString) + 1); } else counts.set(personString, 1); }); console.log(counts);
The resulting output is:
Map(2) { '{"name":"John Doe","age":25}' => 2, '{"name":"Mary Jane","age":24}' => 3 }
Note that there are limitations to this approach. For example, objects with circular references cannot be stringified using JSON.stringify().
Both the techniques for counting with Maps and for counting with objects can be used to count anything, as long as you can find a way to iterate through the items. Some types of data can be converted to an array, for example, strings can be split, and keys of objects can be accessed as an array via Object.keys().
There are also some iterables that don't need to be converted into an array. For example, it's possible to iterate through the characters of a string by using an index. The same principle can be used in array-like structures like HTML Collections.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!