Comment compter certains éléments dans un tableau?


162

J'ai un tableau:

[1, 2, 3, 5, 2, 8, 9, 2]

Je voudrais savoir combien il y a de 2s dans le tableau.

Quelle est la manière la plus élégante de le faire en JavaScript sans boucler avec une forboucle?

Réponses:


90

Très simple:

var count = 0;
for(var i = 0; i < array.length; ++i){
    if(array[i] == 2)
        count++;
}

53
Non, ce que je veux dire, c'est sans boucler avec "for"
Leem

9
@Leem: Pourquoi la boucle est mauvaise? Il y a toujours une boucle à un moment donné. Évidemment, vous créeriez une fonction qui cache la boucle. Ces demandes «Je ne veux pas utiliser le bon outil pour le travail» - n'ont jamais eu beaucoup de sens pour moi. Et nous pouvons discuter de ce qui est le plus élégant. Par exemple, pour moi, faire un appel de fonction par élément juste pour le comparer à une valeur n'est pas élégant.
Felix Kling

2
pour rire: alert (eval ('(' + my_array.join ('== 2) + (') + '== 2)')) jsfiddle.net/gaby_de_wilde/gujbmych
user40521

29
L'OP pense probablement que le bouclage est mauvais car il s'agit de 5 lignes de code et nécessite un état mutable. Un développeur qui viendra lire cela plus tard devra passer un peu de temps pour vérifier ce qu'il fait et perdre le focus de sa tâche. Une abstraction est bien supérieure: const count = countItems(array, 2);et les détails de l'implémentation peuvent être discutés à l'intérieur.
joeytwiddle

2
Ce n'est pas la bonne réponse car la question demande clairement de ne pas utiliser de boucles. Vérifiez ma solution qui n'utilise pas de boucles. stackoverflow.com/a/44743436/8211014
Luis Orantes

289

[ cette réponse est un peu datée: lisez les modifications ]

Dites bonjour à vos amis: mapet filteret reduceet forEachet everyetc.

(J'écris seulement occasionnellement des boucles for en javascript, car la portée au niveau du bloc est manquante, vous devez donc utiliser une fonction comme corps de la boucle de toute façon si vous avez besoin de capturer ou de cloner votre index ou valeur d'itération. For-loops sont généralement plus efficaces, mais parfois vous avez besoin d'une fermeture.)

Le moyen le plus lisible:

[....].filter(x => x==2).length

(On aurait pu écrire à la .filter(function(x){return x==2}).lengthplace)

Ce qui suit est plus économe en espace (O (1) plutôt que O (N)), mais je ne suis pas sûr du montant des avantages / pénalités que vous pourriez payer en termes de temps (pas plus qu'un facteur constant depuis votre visite 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 peut être plus rapide sur certains navigateurs ... vous pouvez tester des choses sur jsperf.com.)


Vous pouvez alors être élégant et en faire une fonction prototype:

[1, 2, 3, 5, 2, 8, 9, 2].count(2)

Comme ça:

Object.defineProperties(Array.prototype, {
    count: {
        value: function(value) {
            return this.filter(x => x==value).length;
        }
    }
});

Vous pouvez également coller l'ancienne technique classique de la boucle for (voir les autres réponses) dans la définition de propriété ci-dessus (encore une fois, ce serait probablement beaucoup plus rapide).


Édition 2017 :

Oups, cette réponse est devenue plus populaire que la bonne réponse. En fait, utilisez simplement 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. Vous devriez donc 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 pouvez définir une version .countStrictEq(...)qui utilise la ===notion d'égalité. La notion d'égalité peut être importante dans ce que vous faites! (par exemple [1,10,3,'10'].count(10)==2, parce que des nombres comme '4' == 4 en javascript ... donc l'appelant .countEqou .countNonstrictinsiste sur l'utilisation de l' ==opérateur.)

Pensez également à utiliser votre propre structure de données multisets (par exemple, comme celle de python ' collections.Counter') 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émo:

> 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)

note de bas de page: Cependant, si vous vouliez toujours la méthode de programmation fonctionnelle (ou un one-liner jetable sans remplacer Array.prototype), vous pouvez l'écrire plus brièvement de nos jours sous la forme [...].filter(x => x==2).length. Si vous vous souciez des performances, notez que même si ces performances sont asymptotiquement identiques à celles de la boucle for (temps O (N)), cela peut nécessiter une mémoire supplémentaire O (N) (au lieu de mémoire O (1)) car elle sera presque certainement générer un tableau intermédiaire, puis compter les éléments de ce tableau intermédiaire.


1
C'est une bonne solution FP, le seul "problème" (sans importance dans la plupart des cas) crée un tableau intermédiaire.
tokland

1
@tokland: Si c'est un problème, vous pouvez le fairearray.reduce(function(total,x){return x==value? : total+1 : total}, 0)
ninjagecko

1
@ninjagecko Ne devrait-il pas y avoir un seul deux-points dans l'opérateur ternaire? [...].reduce(function(total,x){return x==2 ? total+1 : total}, 0)
A.Krueger

2
@tokland Peut-être que le filtre ne créera pas de tableau intermédiaire. Un bon compilateur d'optimisation peut facilement reconnaître que seule la longueur du tableau est utilisée. Peut-être qu'aucun des compilateurs JS actuels n'est assez intelligent pour faire cela, mais ce n'est pas important. Comme le dit Fowler, "Si quelque chose vous fait mal, faites-en davantage". Il est à courte vue d'éviter les carences du compilateur en écrivant du code de mauvaise qualité. Si le compilateur est nul, corrigez le compilateur. mlafeldt.github.io/blog/if-it-hurts-do-it-more-often
John Henckel

Je considère cette solution optimale 2017: const count = (list) => list.filter((x) => x == 2).length. Ensuite, utilisez-le en appelant count(list)where list est un tableau de nombres. Vous pouvez également const count = (list) => list.filter((x) => x.someProp === 'crazyValue').lengthcompter les instances de crazyValue dans le tableau d'objets. Notez qu'il s'agit d'une correspondance exacte pour la propriété.
agm1984

71

Mise à jour ES6 vers JS:

Notez que vous devez toujours utiliser triple égal: ===pour obtenir une comparaison correcte:

// Let has local scope
let array = [1, 2, 3, 5, 2, 8, 9, 2]

// Functional filter with an Arrow function
array.filter(x => x === 2).length  // -> 3

La fonction Arrow unanime suivante (fonction lambda) dans JS:

(x) => {
   const k = 2
   return k * x
}

peut être simplifiée à cette forme concise pour une seule entrée:

x => 2 * x

où le returnest implicite.


La fonction de filtre est-elle plus performante que l'utilisation de la boucle es6 for of?
Niklas

1
@Niklas, je pense que c'est la même chose (car les deux doivent vérifier tous les éléments, O (N)), mais, je suppose que cela dépend du navigateur et aussi du nombre d'éléments et de l'ordinateur pour savoir ce qui tient dans la mémoire cache. Donc, je suppose que la réponse est: "C'est complexe" :)
Sverrisson

67

2017: Si quelqu'un est toujours intéressé par la question, ma solution est la suivante:

const arrayToCount = [1, 2, 3, 5, 2, 8, 9, 2];
const result = arrayToCount.filter(i => i === 2).length;
console.log('number of the found elements: ' + result);


8

Si vous utilisez lodash ou un soulignement, la méthode _.countBy fournira un objet de totaux agrégés indexés par chaque valeur du tableau. Vous pouvez le transformer en une seule ligne si vous n'avez besoin de compter qu'une seule valeur:

_.countBy(['foo', 'foo', 'bar'])['foo']; // 2

Cela fonctionne également très bien sur les tableaux de nombres. Le one-liner pour votre exemple serait:

_.countBy([1, 2, 3, 5, 2, 8, 9, 2])[2]; // 3

5
Énorme exagération. Comme il crée des compteurs pour tous les éléments uniques. Temps et stockage gaspillés.
metalim

5

La façon la plus étrange de faire cela est:

(a.length-(' '+a.join(' ')+' ').split(' '+n+' ').join(' ').match(/ /g).length)+1

Où:

  • a est le tableau
  • n est le nombre à compter dans le tableau

Ma suggestion, utilisez une boucle while ou for ;-)


3

Ne pas utiliser de boucle signifie généralement confier le processus à une méthode qui le fait n'utilise une boucle.

Voici une façon dont notre codeur haineux de boucle peut satisfaire sa haine, à un prix:

var a=[1, 2, 3, 5, 2, 8, 9, 2];

alert(String(a).replace(/[^2]+/g,'').length);


/*  returned value: (Number)
3
*/

Vous pouvez également appeler à plusieurs reprises indexOf, s'il est disponible en tant que méthode de tableau, et déplacer le pointeur de recherche à chaque fois.

Cela ne crée pas de nouveau tableau et la boucle est plus rapide qu'un forEach ou un filtre.

Cela pourrait faire une différence si vous avez un million de membres à regarder.

function countItems(arr, what){
    var count= 0, i;
    while((i= arr.indexOf(what, i))!= -1){
        ++count;
        ++i;
    }
    return count
}

countItems(a,2)

/*  returned value: (Number)
3
*/

2
Vous pouvez réduire votre expression régulière à juste String(a).match(/2/g).length + 1- bien que méfiez-vous de cela, sinon votre implémentation ne fonctionnera pas bien avec des chiffres à deux chiffres.
Gary Green

2
que diriez-vous de [2, 22, 2]?
Oduvan

2

La plupart des solutions publiées utilisant des fonctions de tableau telles que le filtre sont incomplètes car elles ne sont pas paramétrées.

Voici une solution avec laquelle l'élément à compter peut être défini au moment de l'exécution.

function elementsCount(elementToFind, total, number){
    return total += number==elementToFind;
}

var ar = [1, 2, 3, 5, 2, 8, 9, 2];
var elementToFind=2;
var result = ar.reduce(elementsCount.bind(this, elementToFind), 0);

L'avantage de cette approche est qu'elle pourrait facilement changer la fonction pour compter par exemple le nombre d'éléments supérieur à X.

Vous pouvez également déclarer la fonction de réduction en ligne

var ar = [1, 2, 3, 5, 2, 8, 9, 2];
var elementToFind=2;
var result = ar.reduce(function (elementToFind, total, number){
    return total += number==elementToFind;
}.bind(this, elementToFind), 0);

var elementToFind=2; ... function (elementToFind, total, number){ return total += number==elementToFind; }.bind(this, elementToFind) ...est plus difficile à lire et ne donne aucun avantage sur juste ... (acc, x) => acc += number == 2.... J'aime votre utilisation de +=au lieu de acc + (number == 2)cependant. Cela ressemble à un HACK de syntaxe injustifié.
masterxilo

2

Vraiment, pourquoi auriez-vous besoin mapou filterpour cela? reduceest "né" pour ce genre d'opérations:

[1, 2, 3, 5, 2, 8, 9, 2].reduce( (count,2)=>count+(item==val), 0);

c'est tout! (si item==valdans chaque itération, alors 1 sera ajouté à l'accumulateur count, comme truecela résoudra 1).

En tant que fonction:

function countInArray(arr, val) {
   return arr.reduce((count,item)=>count+(item==val),0)
}

Ou, allez-y et étendez vos tableaux:

Array.prototype.count = function(val) {
   return this.reduce((count,item)=>count+(item==val),0)
}

2

Il vaut mieux l'envelopper en fonction:

let countNumber = (array,specificNumber) => {
    return array.filter(n => n == specificNumber).length
}

countNumber([1,2,3,4,5],3) // returns 1

2

Voici un moyen ES2017 + d'obtenir les décomptes de tous les éléments du tableau en O (N):

const arr = [1, 2, 3, 5, 2, 8, 9, 2];
const counts = {};

arr.forEach((el) => {
  counts[el] = counts[el] ? (counts[el] += 1) : 1;
});

Vous pouvez également éventuellement trier la sortie:

const countsSorted = Object.entries(counts).sort(([_, a], [__, b]) => a - b);

console.log (countSorted) pour votre exemple de tableau:

[
  [ '2', 3 ],
  [ '1', 1 ],
  [ '3', 1 ],
  [ '5', 1 ],
  [ '8', 1 ],
  [ '9', 1 ]
]

1

Je crois que vous recherchez une approche fonctionnelle

    const arr = ['a', 'a', 'b', 'g', 'a', 'e'];
    const count = arr.filter(elem => elem === 'a').length;
    console.log(count); // Prints 3

elem === 'a' est la condition, remplacez-la par la vôtre.


Il n'imprimera pas 3, mais 0. Pour résoudre ce problème, votre deuxième ligne devrait être count = arr.filter(elem => elem === 'a').lengthoucount = arr.filter(elem => {return elem === 'a'}).length
WPomier

1

Je suis un fan de la fonction de réduction de js array.

const myArray =[1, 2, 3, 5, 2, 8, 9, 2];
const count = myArray.reduce((count, num) => num === 2 ? count + 1 : count, 0)

En fait, si vous voulez vraiment avoir de la fantaisie, vous pouvez créer une fonction de comptage sur le prototype Array. Ensuite, vous pouvez le réutiliser.

Array.prototype.count = function(filterMethod) {
  return this.reduce((count, item) => filterMethod(item)? count + 1 : count, 0);
} 

Alors fais

const myArray =[1, 2, 3, 5, 2, 8, 9, 2]
const count = myArray.count(x => x==2)

0

Solution par récursivité

function count(arr, value) {
   if (arr.length === 1)    {
      return arr[0] === value ? 1 : 0;
   } else {
      return (arr.shift() === value ? 1 : 0) + count(arr, value);
   }
}

count([1,2,2,3,4,5,2], 2); // 3

1
Est-ce que cela gère un tableau vide?
Andrew Grimm

@AndrewGrimm a raison. Le cas de base est arr.length == 0
Justin Meiners

Belle solution! J'essayais de faire quelque chose en utilisant la récursivité juste pour la pratique et votre exemple était plus élégant que ce que je faisais. C'est certainement un moyen plus complexe que l'utilisation filter, reduceou un moyen simple forLoopet aussi plus coûteux en termes de performances, mais c'est toujours un excellent moyen de le faire avec la récursivité. Mon seul changement est: je pense juste qu'il serait préférable de créer une fonction et d'ajouter un filtre à l'intérieur pour copier le tableau et éviter une mutation du tableau d'origine, puis utiliser le récursif comme fonction interne.
R. Marques

0

var arrayCount = [1,2,3,2,5,6,2,8];
var co = 0;
function findElement(){
    arrayCount.find(function(value, index) {
      if(value == 2)
        co++;
    });
    console.log( 'found' + ' ' + co + ' element with value 2');
}

Je ferais quelque chose comme ça:

var arrayCount = [1,2,3,4,5,6,7,8];

function countarr(){
  var dd = 0;
  arrayCount.forEach( function(s){
    dd++;
  });

  console.log(dd);
}


0

Créez une nouvelle méthode pour la classe Array dans le fichier de niveau principal et utilisez-la partout dans votre projet.

// say in app.js
Array.prototype.occurrence = function(val) {
  return this.filter(e => e === val).length;
}

Utilisez ceci n'importe où dans votre projet -

[1, 2, 4, 5, 2, 7, 2, 9].occurrence(2);
// above line returns 3

0

Voici une seule ligne en javascript.

  1. Utilisez la carte. Trouvez les valeurs correspondantes(v === 2) dans le tableau, en renvoyant un tableau de uns et de zéros.
  2. Utilisez Réduire. Ajoutez toutes les valeurs du tableau pour le nombre total trouvé.
[1, 2, 3, 5, 2, 8, 9, 2]
  .map(function(v) {
    return v === 2 ? 1 : 0;
  })
  .reduce((a, b) => a + b, 0);

Le résultat est 3.


0

Selon la façon dont vous souhaitez l'exécuter:

const reduced = (array, val) => { // self explanatory
    return array.filter((element) => element === val).length;
}

console.log(reduced([1, 2, 3, 5, 2, 8, 9, 2], 2));

// 3

const reducer = (array) => { // array to set > set.forEach > map.set
    const count = new Map();
    const values = new Set(array);
    values.forEach((element)=> {
        count.set(element, array.filter((arrayElement) => arrayElement === element).length);
    });
    return count;
}
console.log(reducer([1, 2, 3, 5, 2, 8, 9, 2]));

// Map(6) {1 => 1, 2 => 3, 3 => 1, 5 => 1, 8 => 1, …}

-7

Vous pouvez utiliser la propriété length dans le tableau JavaScript:

var myarray = [];
var count = myarray.length;//return 0

myarray = [1,2];
count = myarray.length;//return 2

Si vous le filtrez d'abord, vous pouvez utiliser la longueur. ie array.filter (x => x === 2) .length
Scott Blanch
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.