web-dev-qa-db-fra.com

Comptage des occurrences/fréquences des éléments du tableau

En Javascript, j'essaie de prendre un tableau initial de valeurs numériques et de compter les éléments qu'il contient. Idéalement, le résultat serait deux nouveaux tableaux, le premier spécifiant chaque élément unique et le second contenant le nombre d'occurrences de chaque élément. Cependant, je suis ouvert aux suggestions sur le format de la sortie.

Par exemple, si le tableau initial était:

5, 5, 5, 2, 2, 2, 2, 2, 9, 4

Ensuite, deux nouveaux tableaux seront créés. Le premier contiendrait le nom de chaque élément unique:

5, 2, 9, 4

La seconde contiendrait le nombre d'occurrences de cet élément dans le tableau initial:

3, 5, 1, 1

Étant donné que le nombre 5 apparaît trois fois dans la matrice initiale, le nombre 2 apparaît cinq fois et 9 et 4 apparaissent tous deux une fois.

J'ai beaucoup cherché une solution, mais rien ne semble fonctionner et tout ce que j'ai moi-même essayé est devenu d'une complexité ridicule. Toute aide serait appréciée!

Merci :)

157
Jack W

Voici: 

function foo(arr) {
    var a = [], b = [], prev;

    arr.sort();
    for ( var i = 0; i < arr.length; i++ ) {
        if ( arr[i] !== prev ) {
            a.Push(arr[i]);
            b.Push(1);
        } else {
            b[b.length-1]++;
        }
        prev = arr[i];
    }

    return [a, b];
}

Démo en direct:http://jsfiddle.net/simevidas/bnACW/

Remarque

Ceci change l'ordre du tableau d'entrée d'origine en utilisant Array.sort

73
Šime Vidas

Vous pouvez utiliser un objet pour conserver les résultats:

var arr = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
var counts = {};

for (var i = 0; i < arr.length; i++) {
  var num = arr[i];
  counts[num] = counts[num] ? counts[num] + 1 : 1;
}

console.log(counts[5], counts[2], counts[9], counts[4]);

Donc, maintenant votre objet count peut vous dire quel est le compte pour un nombre particulier:

console.log(counts[5]); // logs '3'

Si vous souhaitez obtenir un tableau de membres, utilisez simplement les fonctions keys()

keys(counts); // returns ["5", "2", "9", "4"]
168
typeof
var a = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4].reduce(function (acc, curr) {
  if (typeof acc[curr] == 'undefined') {
    acc[curr] = 1;
  } else {
    acc[curr] += 1;
  }

  return acc;
}, {});

// a == {2: 5, 4: 1, 5: 3, 9: 1}
72
adamse

Si vous utilisez un trait de soulignement ou un lodash, c’est la chose la plus simple à faire:

_.countBy(array);

Tel que:

_.countBy([5, 5, 5, 2, 2, 2, 2, 2, 9, 4])
=> Object {2: 5, 4: 1, 5: 3, 9: 1}

Comme l'ont souligné d'autres personnes, vous pouvez ensuite exécuter les fonctions _.keys() et _.values() sur le résultat pour obtenir uniquement les numéros uniques et leurs occurrences, respectivement. Mais selon mon expérience, l'objet original est beaucoup plus facile à gérer.

63
radicand

N'utilisez pas deux tableaux pour le résultat, utilisez un objet:

a      = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
result = { };
for(var i = 0; i < a.length; ++i) {
    if(!result[a[i]])
        result[a[i]] = 0;
    ++result[a[i]];
}

Alors result ressemblera à:

{
    2: 5,
    4: 1,
    5: 3,
    9: 1
}
48
mu is too short

Je pense que c'est le moyen le plus simple de compter les occurrences avec la même valeur dans un tableau.

var a = [true, false, false, false];
a.filter(function(value){
    return value === false;
}).length
29
Dmytro Kozlovskyi

const data = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]

function count(arr) {
  return arr.reduce((prev, curr) => (prev[curr] = ++prev[curr] || 1, prev), {})
}

console.log(count(data))

24
Vlad Bezden

La version ES6 devrait être beaucoup plus simple (une autre solution en une ligne)

let arr = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
let acc = arr.reduce((acc, val) => acc.set(val, 1 + (acc.get(val) || 0)), new Map());

console.log(acc);
// output: Map { 5 => 3, 2 => 5, 9 => 1, 4 => 1 }

Une carte au lieu d'un objet simple nous aidant à distinguer différents types d'éléments, sinon tous les calculs sont basés sur des chaînes

13
William Leung

Si vous utilisez un trait de soulignement, vous pouvez passer par la route fonctionnelle

a = ['foo', 'foo', 'bar'];

var results = _.reduce(a,function(counts,key){ counts[key]++; return counts },
                  _.object( _.map( _.uniq(a), function(key) { return [key, 0] })))

donc votre premier tableau est

_.keys(results)

et le second tableau est

_.values(results)

la plupart de ces fonctions seront par défaut des fonctions javascript natives si elles sont disponibles 

démo: http://jsfiddle.net/dAaUU/

8
jhnstn

Sur la base de answer of @adamse and @pmandell (que je vote plus tôt), dans ES6 vous pouvez le faire en une ligne:

  • 2017 edit: J'utilise || pour réduire la taille du code et le rendre plus lisible.

var a=[7,1,7,2,2,7,3,3,3,7,,7,7,7];
alert(JSON.stringify(

a.reduce((r,k)=>{r[k]=1+r[k]||1;return r},{})

));


Il peut être utilisé pour compter les caractères:

var s="ABRACADABRA";
alert(JSON.stringify(

s.split('').reduce((a, c)=>{a[c]++?0:a[c]=1;return a},{})

));

7
ESL

Vous pouvez étendre le prototype Array, comme ceci:

Array.prototype.frequencies = function() {
    var l = this.length, result = {all:[]};
    while (l--){
       result[this[l]] = result[this[l]] ? ++result[this[l]] : 1;
    }
    // all pairs (label, frequencies) to an array of arrays(2)
    for (var l in result){
       if (result.hasOwnProperty(l) && l !== 'all'){
          result.all.Push([ l,result[l] ]);
       }
    }
    return result;
};

var freqs = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4].frequencies();
alert(freqs[2]); //=> 5
// or
var freqs = '1,1,2,one,one,2,2,22,three,four,five,three,three,five'
             .split(',')
             .frequencies();
alert(freqs.three); //=> 3

Sinon, vous pouvez utiliser Array.map:

  Array.prototype.frequencies  = function () {
    var freqs = {sum: 0}; 
    this.map( function (a){ 
        if (!(a in this)) { this[a] = 1; } 
        else { this[a] += 1; }
        this.sum += 1;
        return a; }, freqs
    );
    return freqs;
  }
5
KooiInc

Voici juste quelque chose de léger et facile pour les yeux ...

function count(a,i){
 var result = 0;
 for(var o in a)
  if(a[o] == i)
   result++;
 return result;
}

Edit: Et puisque vous voulez toutes les occurrences ...

function count(a){
 var result = {};
 for(var i in a){
  if(result[a[i]] == undefined) result[a[i]] = 0;
  result[a[i]]++;
 }
 return result;
}
5
ElDoRado1239

Solution ES6 à une ligne. Autant de réponses utilisant objet comme carte mais je ne vois personne utiliser une réelle Carte

const map = arr.reduce((acc, e) => acc.set(e, acc.get(e) + 1 || 1), new Map());

Utilisez map.keys() pour obtenir des éléments uniques

Utilisez map.values() pour obtenir les occurrences

Utilisez map.entries() pour obtenir les paires [élément, fréquence]

Je suis un peu en retard à la fête mais j'espère qu'au moins une personne le trouvera utile.

var arr = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]

const map = arr.reduce((acc, e) => acc.set(e, acc.get(e) + 1 || 1), new Map());

console.info(...map.keys())
console.info(...map.values())
console.info(...map.entries())
4
Tomasz Zieliński
var array = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];

function countDuplicates(obj, num){
  obj[num] = (++obj[num] || 1);
  return obj;
}

var answer = array.reduce(countDuplicates, {});
// answer => {2:5, 4:1, 5:3, 9:1};

Si vous voulez toujours deux tableaux, vous pouvez utiliser answer comme ceci ...

var uniqueNums = Object.keys(answer);
// uniqueNums => ["2", "4", "5", "9"];

var countOfNums = Object.keys(answer).map(key => answer[key]);
// countOfNums => [5, 1, 3, 1];

Ou si vous voulez que les nombres uniques soient des nombres

var uniqueNums = Object.keys(answer).map(key => +key);
// uniqueNums => [2, 4, 5, 9];
4
SoEzPz

Solution ES6 avec réduction (fixe):

const arr = [2, 2, 2, 3, 2]

const count = arr.reduce((pre, cur) => (cur === 2) ? ++pre : pre, 0)
console.log(count) // 4

3
Thomas Gotwig

En ce qui concerne mon commentaire demandant @Emissary sur un ajustement à sa solution. En ajoutant la façon dont je l'ai géré:

let distinctArr = yourArray.filter((curElement, index, array) => array.findIndex(t =>    t.prop1=== curElement.prop1 && t.prop2 === curElement.prop2 && t.prop3=== curElement.prop3) === index);
let distinctWithCount = [...new Set(distinctArr)].map(function(element){element.prop4 = yourArray.filter(t =>    t.prop1=== element.prop1 && t.prop2 === element.prop2 && t.prop2=== element.prop2).length;

Ce que je fais ici est de supprimer les doublons et de sauvegarder le tableau (distinctArr), puis de compter sur le tableau original (yourArray) la durée de duplication de l'objet et d'ajouter une 4ème propriété avec la valeur des occurrences 

J'espère que cela aidera pour quelqu'un qui a besoin de cette solution spécifique .__ Ofc il est fait avec ES6

1
sharon gur
function countOcurrences(arr){
    return arr.reduce((aggregator, value, index, array) => {
      if(!aggregator[value]){
        return aggregator = {...aggregator, [value]: 1};  
      }else{
        return aggregator = {...aggregator, [value]:++aggregator[value]};
      }
    }, {})
}
1
José Salgado

Juste un autre point de vue ...

let array = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]
let count = 0
let finalResult = []
array.sort().map((e, index)=>{
    if(e==array[index+1]){
        count++
    } else {
        count++
        finalResult.Push([e+" => "+count])
        count = 0
    }
})
console.log(finalResult)

// output
// [ [ '2 => 5' ], [ '4 => 1' ], [ '5 => 3' ], [ '9 => 1' ] ]
1
Miguel Afonso

Découvrez le code ci-dessous.

<html>
<head>
<script>
// array with values
var ar = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];

var Unique = []; // we'll store a list of unique values in here
var Counts = []; // we'll store the number of occurances in here

for(var i in ar)
{
    var Index = ar[i];
    Unique[Index] = ar[i];
    if(typeof(Counts[Index])=='undefined')  
        Counts[Index]=1;
    else
        Counts[Index]++;
}

// remove empty items
Unique = Unique.filter(function(){ return true});
Counts = Counts.filter(function(){ return true});

alert(ar.join(','));
alert(Unique.join(','));
alert(Counts.join(','));

var a=[];

for(var i=0; i<Unique.length; i++)
{
    a.Push(Unique[i] + ':' + Counts[i] + 'x');
}
alert(a.join(', '));

</script>
</head>
<body>

</body>
</html>
1

Essaye ça:

Array.prototype.getItemCount = function(item) {
    var counts = {};
    for(var i = 0; i< this.length; i++) {
        var num = this[i];
        counts[num] = counts[num] ? counts[num]+1 : 1;
    }
    return counts[item] || 0;
}
1
Aamir Afridi

Alors voici comment je le ferais avec certaines des dernières fonctionnalités javascript:

Tout d’abord, réduisez le tableau à un Map des nombres:

let countMap = array.reduce(
  (map, value) => {map.set(value, (map.get(value) || 0) + 1); return map}, 
  new Map
)

En utilisant une Map, votre tableau de départ peut contenir n’importe quel type d’objet et le nombre sera correct. Sans Map, certains types d’objets vous donneront d’étranges comptages . Consultez la Map docs pour plus d’informations sur les différences.

Cela peut également être fait avec un objet si toutes vos valeurs sont des symboles, des nombres ou des chaînes:

let countObject = array.reduce(
  (map, value) => { map[value] = (map[value] || 0) + 1; return map },
  {}
)

Ou légèrement raffiné de manière fonctionnelle sans mutation, en utilisant la syntaxe de déstructuration et de propagation d'objet:

let countObject = array.reduce(
  (value, {[value]: count = 0, ...rest}) => ({ [value]: count + 1, ...rest }),
  {}
)

À ce stade, vous pouvez utiliser la variable Map ou un objet pour vos comptages (et la carte est directement itérable, contrairement à un objet), ou la convertir en deux tableaux.

Pour la Map:

countMap.forEach((count, value) => console.log(`value: ${value}, count: ${count}`)

let values = countMap.keys()
let counts = countMap.values()

Ou pour l'objet:

Object
  .entries(countObject) // convert to array of [key, valueAtKey] pairs
  .forEach(([value, count]) => console.log(`value: ${value}, count: ${count}`)

let values = Object.keys(countObject)
let counts = Object.values(countObject)
1
Garrett Motzner
var aa = [1,3,5,7,3,2,4,6,8,1,3,5,5,2,0,6,5,9,6,3,5,2,5,6,8];
var newArray = {};
for(var element of aa){
  if(typeof newArray[element] === 'undefined' || newArray[element] === null){
    newArray[element] = 1;
  }else{
    newArray[element] +=1;
  }
}

for ( var element in newArray){
  console.log( element +" -> "+ newArray[element]);
}
0
Dilraj Singh

Vous pouvez rendre cela beaucoup plus facile en étendant vos tableaux avec une fonction count. Cela fonctionne un peu comme le Array#count de Rails, si vous le connaissez bien.

Array.prototype.count = function(obj){
    var count = this.length;
    if(typeof(obj) !== "undefined"){
        var array = this.slice(0), count = 0; // clone array and reset count
        for(i = 0; i < array.length; i++){
            if(array[i] == obj){
                count++;
            }
        }
    }
    return count;
}

Usage:

var array = ['a', 'a', 'b', 'c'];
array.count('a'); // => 2
array.count('b'); // => 1
array.count('d'); // => 0
array.count(); // => 4

Source (Gist)

0
zykadelic

Voici un moyen de compter les occurrences dans un tableau d'objets. Il place également le contenu du premier tableau dans un nouveau tableau pour trier les valeurs afin que l'ordre dans le tableau d'origine ne soit pas perturbé. Ensuite, une fonction récursive est utilisée pour parcourir chaque élément et compter la propriété de quantité de chaque objet dans le tableau.

var big_array = [
  { name: "Pineapples", quantity: 3 },
  { name: "Pineapples", quantity: 1 },
  { name: "Bananas", quantity: 1 },
  { name: "Limes", quantity: 1 },
  { name: "Bananas", quantity: 1 },
  { name: "Pineapples", quantity: 2 },
  { name: "Pineapples", quantity: 1 },
  { name: "Bananas", quantity: 1 },
  { name: "Bananas", quantity: 1 },
  { name: "Bananas", quantity: 5 },
  { name: "Coconuts", quantity: 1 },
  { name: "Lemons", quantity: 2 },
  { name: "Oranges", quantity: 1 },
  { name: "Lemons", quantity: 1 },
  { name: "Limes", quantity: 1 },
  { name: "Grapefruit", quantity: 1 },
  { name: "Coconuts", quantity: 5 },
  { name: "Oranges", quantity: 6 }
];

function countThem() {
  var names_array = [];
  for (var i = 0; i < big_array.length; i++) {
    names_array.Push( Object.assign({}, big_array[i]) );
  }

  function outerHolder(item_array) {
    if (item_array.length > 0) {
      var occurrences = [];
      var counter = 0;
      var bgarlen = item_array.length;
      item_array.sort(function(a, b) { return (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0); });

      function recursiveCounter() {
        occurrences.Push(item_array[0]);
        item_array.splice(0, 1);
        var last_occurrence_element = occurrences.length - 1;
        var last_occurrence_entry = occurrences[last_occurrence_element].name;
        var occur_counter = 0;
        var quantity_counter = 0;
        for (var i = 0; i < occurrences.length; i++) {
          if (occurrences[i].name === last_occurrence_entry) {
            occur_counter = occur_counter + 1;
            if (occur_counter === 1) {
              quantity_counter = occurrences[i].quantity;
            } else {
              quantity_counter = quantity_counter + occurrences[i].quantity;
            }
          }
        }

        if (occur_counter > 1) {
          var current_match = occurrences.length - 2;
          occurrences[current_match].quantity = quantity_counter;
          occurrences.splice(last_occurrence_element, 1);
        }

        counter = counter + 1;

        if (counter < bgarlen) {
          recursiveCounter();
        }
      }

      recursiveCounter();

      return occurrences;
    }
  }
  alert(JSON.stringify(outerHolder(names_array)));
}
0
nate_js

Pour ce faire, il existe un moyen beaucoup plus simple et efficace d'utiliser ramda.js. Exemple de code ici

const ary = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]; R.countBy(r=> r)(ary) countBy la documentation est à documentation

Voici la solution la plus simple

const data = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
let occurance_arr=[]; 
const aCount =  [...new Set(data)].map(x => {
   occurance_arr.Push(data.filter(y=> y==x).length)
});
console.log(occurance_arr);   //[3, 5, 1, 1]
0
Ankur

Je résolvais un problème similaire sur les codewars et conçu la solution suivante qui a fonctionné pour moi.

Cela donne le nombre le plus élevé d'un entier dans un tableau et également le nombre entier lui-même. Je pense que cela peut aussi s'appliquer au tableau de chaînes. 

Pour trier correctement les chaînes, supprimez la function(a, b){return a-b} de la partie sort().

function mostFrequentItemCount(collection) {
    collection.sort(function(a, b){return a-b});
    var i=0;
    var ans=[];
    var int_ans=[];
    while(i<collection.length)
    {
        if(collection[i]===collection[i+1])
        {
            int_ans.Push(collection[i]);
        }
        else
        {
            int_ans.Push(collection[i]);
            ans.Push(int_ans);
            int_ans=[];
        }
        i++;
    }

    var high_count=0;
    var high_ans;

    i=0;
    while(i<ans.length)
    {
        if(ans[i].length>high_count)
        {
            high_count=ans[i].length;
            high_ans=ans[i][0];
        }
        i++;
    }
    return high_ans;
}
0
Varun Upadhyay

En utilisantMAPvous pouvez avoir 2 tableaux dans la sortie: l'un contenant les occurrences et l'autre contenant le nombre d'occurrences.

const dataset = [2,2,4,2,6,4,7,8,5,6,7,10,10,10,15];
let values = [];
let keys = [];

var mapWithOccurences = dataset.reduce((a,c) => {
  if(a.has(c)) a.set(c,a.get(c)+1);
  else a.set(c,1);
  return a;
}, new Map())
.forEach((value, key, map) => {
  keys.Push(key);
  values.Push(value);
});


console.log(keys)
console.log(values)
0
Melchia

Cette question a plus de 8 ans et beaucoup de réponses ne tiennent pas vraiment en tenant compte de ES6 et de ses nombreux avantages.

Il est peut-être encore plus important de penser aux conséquences de notre code pour la gestion de la récupération de place/de la mémoire chaque fois que nous créons des tableaux supplémentaires. double ou triple copie de tableaux ou même convertir des tableaux en objets. Ce sont des observations triviales pour de petites applications, mais si l’échelle est un objectif à long terme, réfléchissez-y à fond.

Si vous avez juste besoin d'un "compteur" pour des types de données spécifiques et que le point de départ est un tableau (je suppose que vous voulez donc une liste ordonnée et tirez parti des nombreuses propriétés et méthodes offertes par les tableaux), vous pouvez simplement effectuer une itération dans array1 et la remplir. array2 avec les valeurs et le nombre d'occurrences de ces valeurs trouvées dans array1.

Aussi simple que cela.

Exemple de classe simple SimpleCounter (ES6) pour Programmation orientée objet et Conception orientée objet

class SimpleCounter { 

    constructor(rawList){ // input array type
        this.rawList = rawList;
        this.finalList = [];
    }

    mapValues(){ // returns a new array

        this.rawList.forEach(value => {
            this.finalList[value] ? this.finalList[value]++ : this.finalList[value] = 1;
        });

        this.rawList = null; // remove array1 for garbage collection

        return this.finalList;

    }

}

module.exports = SimpleCounter;
0
rags2riches

C'est facile avec filtre

Dans cet exemple, nous affectons simplement le nombre, la longueur du tableau filtrée par la clé que vous recherchez.

let array = [{name: "steve", age: 22}, {name: "bob", age: 30}]

let count = array.filter(obj => obj.name === obj.name).length

console.log(count)

plus d'informations sur JS Filiters ici https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

0
Alex Wood

Ma solution avec ramda:

const testArray = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]

const counfFrequency = R.compose(
  R.map(R.length),
  R.groupBy(R.identity),
)

counfFrequency(testArray)

Lien vers REPL.

0
Michal

Tableau donné x I.e x = ['boy','man','oldman','scout','pilot']; Nombre d'occurrences d'un élément 'man' est 

x.length - x.toString().split(',man,').toString().split(',').length ;
0