Come contare determinati elementi nell'array?


162

Ho un array:

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

Vorrei sapere quanti 2s sono nell'array.

Qual è il modo più elegante per farlo in JavaScript senza loop con forloop?

Risposte:


90

Molto semplice:

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

53
No, quello che voglio dire è senza fare un giro con "for"
Leem

9
@Leem: Perché il looping è male? C'è sempre un ciclo ad un certo punto. Ovviamente creeresti una funzione che nasconde il loop. Queste richieste "Non voglio usare lo strumento giusto per il lavoro" non hanno mai avuto molto senso per me. E possiamo discutere di ciò che è più elegante. Ad esempio, per me, effettuare una chiamata di funzione per elemento solo per confrontarlo con un valore non è elegante.
Felix Kling,

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

29
L'OP probabilmente pensa che il looping sia negativo perché è composto da 5 righe di codice e richiede uno stato mutabile. Uno sviluppatore che viene a leggere che in seguito dovrà dedicare un po 'di tempo per verificare cosa fa e perdere la concentrazione dal proprio compito. Un'astrazione è di gran lunga superiore: const count = countItems(array, 2);e i dettagli di implementazione possono essere discussi all'interno.
joeytwiddle,

2
Questa non è la risposta giusta perché la domanda chiaramente richiede di non utilizzare i loop. Controlla la mia soluzione che non fa uso di loop. stackoverflow.com/a/44743436/8211014
Luis Orantes

289

[ questa risposta è un po 'datata: leggi le modifiche ]

Saluta i tuoi amici: mape filterereduce e forEache everyecc

(Scrivo solo occasionalmente for-loops in javascript, a causa della mancanza di scoping a livello di blocco, quindi devi usare una funzione come corpo del loop comunque se devi catturare o clonare il tuo indice o valore di iterazione. For-loops sono generalmente più efficienti, ma a volte è necessaria una chiusura.)

Il modo più leggibile:

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

(Avremmo potuto .filter(function(x){return x==2}).lengthinvece scrivere )

Quanto segue è più efficiente in termini di spazio (O (1) anziché O (N)), ma non sono sicuro di quanto beneficio / penalità potresti pagare in termini di tempo (non più di un fattore costante da quando visiti ogni elemento esattamente una volta):

[....].reduce((total,x) => (x==2 ? total+1 : total), 0)

(Se hai bisogno di ottimizzare questo particolare pezzo di codice, un ciclo for potrebbe essere più veloce su alcuni browser ... puoi testare le cose su jsperf.com.)


Puoi quindi essere elegante e trasformarlo in una funzione prototipo:

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

Come questo:

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

Puoi anche applicare la vecchia tecnica for-loop (vedi altre risposte) all'interno della definizione della proprietà sopra (di nuovo, probabilmente sarebbe molto più veloce).


Modifica 2017 :

Spiacenti, questa risposta è diventata più popolare della risposta corretta. In realtà, basta usare la risposta accettata. Sebbene questa risposta possa essere carina, i compilatori js probabilmente non ottimizzano (o non possono a causa delle specifiche) tali casi. Quindi dovresti davvero scrivere un semplice ciclo:

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

È possibile definire una versione .countStrictEq(...)che utilizza la ===nozione di uguaglianza. La nozione di uguaglianza può essere importante per quello che stai facendo! (ad esempio [1,10,3,'10'].count(10)==2, perché numeri come '4' == 4 in javascript ... quindi chiamandolo .countEqo .countNonstrictsottolinea che usa il== operatore.)

Considera anche di usare la tua struttura di dati multiset (ad esempio come quella di Python ' collections.Counter') per evitare di dover fare il conteggio in primo luogo.

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 {}
    }
}

demo:

> 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: Tuttavia, se volevi ancora la modalità di programmazione funzionale (o un one-liner usa e getta senza sovrascrivere Array.prototype), oggi potresti scriverlo in maniera più concisa come [...].filter(x => x==2).length. Se ti preoccupi delle prestazioni, nota che mentre questa è asintoticamente la stessa performance del tempo for-loop (O (N)), potrebbe richiedere O (N) memoria aggiuntiva (invece di O (1) memoria) perché quasi certamente genera un array intermedio e quindi conta gli elementi di tale array intermedio.


1
Questa è una buona soluzione FP, l'unico "problema" (irrilevante per la maggior parte dei casi) crea un array intermedio.
tokland

1
@tokland: Se questo è un problema, puoi farloarray.reduce(function(total,x){return x==value? : total+1 : total}, 0)
ninjagecko l'

1
@ninjagecko Non dovrebbero esserci solo due punti nell'operatore ternario? [...].reduce(function(total,x){return x==2 ? total+1 : total}, 0)
A.Krueger,

2
@tokland Forse il filtro non creerà un array intermedio. Un buon compilatore di ottimizzazione può facilmente riconoscere che viene utilizzata solo la lunghezza dell'array. Forse nessuno degli attuali compilatori JS è abbastanza intelligente da farlo, ma questo non è importante. Come dice Fowler, "Se qualcosa fa male, allora fai di più". È miope evitare carenze del compilatore scrivendo un codice scadente. Se il compilatore fa schifo, correggi il compilatore. mlafeldt.github.io/blog/if-it-hurts-do-it-more-often
John Henckel

Secondo me è una soluzione ottimale 2017: const count = (list) => list.filter((x) => x == 2).length. Quindi usalo chiamando count(list)dove list è un array di numeri. Puoi anche fare il const count = (list) => list.filter((x) => x.someProp === 'crazyValue').lengthconteggio delle istanze di crazyValue nella matrice di oggetti. Nota, è una corrispondenza esatta per la proprietà.
agm1984,

71

Aggiornamento ES6 a JS:

Nota che dovresti sempre usare triple egals: ===per ottenere un confronto corretto:

// 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 seguente funzione freccia unanime (funzione lambda) in JS:

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

può essere semplificato in questa forma concisa per un singolo input:

x => 2 * x

dove returnè implicito.


La funzione filtro è più performante dell'uso di es6 per of loop?
Niklas,

1
@Niklas, penso che sia lo stesso (dato che entrambi devono controllare tutti gli elementi, O (N)), ma, suppongo che dipenda dal browser e anche dal numero di elementi e computer per ciò che si adatta alla memoria cache. Quindi, immagino che la risposta sia: "È complesso" :)
Sverrisson il

67

2017: se qualcuno è ancora interessato alla domanda, la mia soluzione è la seguente:

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

Se si utilizza lodash o il carattere di sottolineatura, il metodo _.countBy fornirà un oggetto di totali aggregati digitato da ciascun valore dell'array. Puoi trasformarlo in una riga unica se devi contare solo un valore:

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

Questo funziona bene anche su matrici di numeri. Il one-liner per il tuo esempio sarebbe:

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

5
Enorme esagerazione. Come crea contatori per tutti gli elementi unici. Conservazione e tempo sprecati.
metalim

5

Il modo più strano che posso pensare di fare questo è:

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

Dove:

  • a è l'array
  • n è il numero da contare nell'array

Il mio consiglio, usa un po 'o per il ciclo ;-)


3

Non usare un ciclo di solito significa consegnare il processo ad un metodo che lo fa utilizza un ciclo.

Ecco un modo in cui il nostro programmatore che odia i loop può soddisfare il suo odio, ad un prezzo:

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

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


/*  returned value: (Number)
3
*/

Puoi anche chiamare ripetutamente indexOf, se è disponibile come metodo array, e spostare il puntatore di ricerca ogni volta.

Ciò non crea un nuovo array e il ciclo è più veloce di un forEach o di un filtro.

Potrebbe fare la differenza se hai un milione di membri da guardare.

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
Potresti ridurre la tua regex a solo String(a).match(/2/g).length + 1- anche se fai attenzione o la tua implementazione non funzionerà bene con le doppie cifre.
Gary Green,

2
che ne dici di [2, 22, 2]?
Oduvan,

2

La maggior parte delle soluzioni pubblicate che utilizzano funzioni di array come il filtro sono incomplete perché non sono parametrizzate.

Ecco una soluzione con la quale l'elemento da contare può essere impostato in fase di esecuzione.

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

Il vantaggio di questo approccio è che potrebbe facilmente cambiare la funzione per contare, ad esempio, il numero di elementi maggiore di X.

Puoi anche dichiarare la funzione di riduzione in linea

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) ...è più difficile da leggere e non offre alcun vantaggio rispetto al solo ... (acc, x) => acc += number == 2.... Mi piace il tuo uso +=invece che acc + (number == 2)però. Sembra però una sintassi ingiustificata HACK.
masterxilo,

2

Davvero, perché avresti bisogno mapo filterper questo? reduceè "nato" per questo tipo di operazioni:

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

questo è tutto! (se item==valin ogni iterazione, allora 1 verrà aggiunto all'accumulatore count, come truerisolverà a 1).

Come una funzione:

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

Oppure, vai avanti ed estendi le tue matrici:

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

2

È meglio avvolgerlo in funzione:

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

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

2

Ecco un modo ES2017 + per ottenere i conteggi per tutti gli elementi dell'array in 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;
});

È inoltre possibile ordinare l'output facoltativamente:

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

console.log (countSorted) per il tuo array di esempio:

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

1

Credo che quello che stai cercando sia un approccio funzionale

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

elem === 'a' è la condizione, sostituiscila con la tua.


Non stampa 3, ma 0. Per risolvere il problema, la seconda riga dovrebbe essere count = arr.filter(elem => elem === 'a').lengthocount = arr.filter(elem => {return elem === 'a'}).length
WPomier

1

Sono un fan iniziale della funzione di riduzione di js array.

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

In effetti, se vuoi davvero fantasticare, puoi creare una funzione di conteggio sul prototipo di array. Quindi puoi riutilizzarlo.

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

Quindi fa

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

0

Soluzione per ricorsione

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
Questo gestisce un array vuoto?
Andrew Grimm,

@AndrewGrimm ha ragione. Il caso base è arr.length == 0
Justin Meiners il

Bella soluzione! Stavo cercando di fare qualcosa usando la ricorsione solo per fare pratica e il tuo esempio era più elegante di quello che stavo facendo. È sicuramente un modo più complesso rispetto all'utilizzo filter, reduceo un semplice forLoop, e anche, più costoso quando si guarda alle prestazioni, ma è comunque un ottimo modo di farlo con la ricorsione. La mia unica modifica è: penso solo che sarebbe meglio creare una funzione e aggiungere un filtro al suo interno per copiare l'array ed evitare una mutazione dell'array originale, quindi utilizzare il ricorsivo come funzione interna.
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');
}

Vorrei fare qualcosa del genere:

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

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

  console.log(dd);
}


0

Crea un nuovo metodo per la classe Array nel file di livello principale e usalo in tutto il tuo progetto.

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

Usalo ovunque nel tuo progetto -

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

0

Ecco una riga in javascript.

  1. Usa la mappa. Trova i valori corrispondenti(v === 2) nell'array, restituendo un array di uno e zero.
  2. Usa Riduci. Aggiungi tutti i valori dell'array per il numero totale trovato.
[1, 2, 3, 5, 2, 8, 9, 2]
  .map(function(v) {
    return v === 2 ? 1 : 0;
  })
  .reduce((a, b) => a + b, 0);

Il risultato è 3.


0

A seconda di come si desidera eseguirlo:

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

È possibile utilizzare la proprietà length nell'array JavaScript:

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

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

Se lo filtri per primo, puoi usare la lunghezza. vale a dire array.filter (x => x === 2) .length
Scott Blanch
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.