Jak policzyć określone elementy w tablicy?

162

Mam tablicę:

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

Chciałbym wiedzieć, ile 2 s znajduje się w tablicy.

Jaki jest najbardziej elegancki sposób zrobienia tego w JavaScript bez zapętlenia z forpętlą?

Leem
źródło

Odpowiedzi:

90

Bardzo prosty:

var count = 0;
for(var i = 0; i < array.length; ++i){
    if(array[i] == 2)
        count++;
}
Thor Jacobsen
źródło
53
Nie, mam na myśli to, że bez zapętlenia z „for”
Leem,
9
@Leem: Dlaczego zapętlenie jest złe? W pewnym momencie zawsze występuje zapętlenie. Oczywiście stworzyłbyś funkcję, która ukrywa pętlę. Żądania typu „Nie chcę używać odpowiedniego narzędzia do pracy” - nigdy nie miały dla mnie sensu. I możemy dyskutować, co jest najbardziej eleganckie. Na przykład dla mnie wywołanie funkcji dla każdego elementu w celu porównania go z wartością nie jest eleganckie.
Felix Kling
2
do śmiechu: alert (eval ('(' + my_array.join ('== 2) + (') + '== 2)')) jsfiddle.net/gaby_de_wilde/gujbmych
user40521
29
OP prawdopodobnie uważa, że ​​pętla jest zła, ponieważ składa się z 5 linii kodu i wymaga mutowalnego stanu. Deweloper, który później to przeczyta, będzie musiał poświęcić trochę czasu na sprawdzenie, co robi, i stracić koncentrację na swoim zadaniu. Abstrakcja jest znacznie lepsza: const count = countItems(array, 2);a szczegóły implementacji można dyskutować wewnątrz.
joeytwiddle
2
To nie jest właściwa odpowiedź, ponieważ pytanie wyraźnie dotyczy nie używania pętli. Sprawdź moje rozwiązanie, które nie wykorzystuje pętli. stackoverflow.com/a/44743436/8211014
Luis Orantes
289

[ ta odpowiedź jest nieco przestarzała: przeczytaj zmiany ]

Przywitaj się ze znajomymi: mapi filteri reducei forEachi everyitd.

(Tylko sporadycznie piszę pętle for w javascript, ponieważ brakuje zakresu na poziomie bloku, więc i tak musisz użyć funkcji jako ciała pętli, jeśli chcesz przechwycić lub sklonować indeks lub wartość iteracji. generalnie są bardziej wydajne, ale czasami potrzebujesz zamknięcia).

Najbardziej czytelny sposób:

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

( .filter(function(x){return x==2}).lengthZamiast tego moglibyśmy napisać )

Poniższe dane są bardziej wydajne przestrzennie (O (1) zamiast O (N)), ale nie jestem pewien, ile korzyści / kary możesz zapłacić pod względem czasu (nie więcej niż stały współczynnik od czasu wizyty każdy element dokładnie raz):

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

(Jeśli chcesz zoptymalizować ten konkretny fragment kodu, pętla for może być szybsza w niektórych przeglądarkach ... możesz przetestować rzeczy na jsperf.com).


Możesz wtedy być elegancki i przekształcić go w funkcję prototypową:

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

Lubię to:

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

Możesz również umieścić zwykłą starą technikę pętli for (zobacz inne odpowiedzi) wewnątrz powyższej definicji właściwości (znowu, prawdopodobnie będzie to znacznie szybsze).


Edycja 2017 :

Ups, ta odpowiedź stała się bardziej popularna niż poprawna odpowiedź. Właściwie po prostu użyj zaakceptowanej odpowiedzi. Chociaż ta odpowiedź może być urocza, kompilatory js prawdopodobnie nie (lub nie mogą z powodu specyfikacji) optymalizować takich przypadków. Więc naprawdę powinieneś napisać prostą pętlę 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;
        }
    }
});

Możesz zdefiniować wersję, w .countStrictEq(...)której używane jest ===pojęcie równości. Pojęcie równości może być ważne dla tego, co robisz! (na przykład [1,10,3,'10'].count(10)==2, ponieważ liczby takie jak „4” == 4 w javascript ... dlatego wywołując je .countEqlub .countNonstrictpodkreślają, że używa ==operatora).

Rozważ także użycie własnej wielozbiorowej struktury danych (np. Jak w Pythonie ' collections.Counter'), aby uniknąć konieczności liczenia w pierwszej kolejności.

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

Próbny:

> 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: Chociaż, jeśli nadal chcesz funkcjonalnego sposobu programowania (lub jednorazowego, jednowierszowego tekstu bez nadpisywania Array.prototype), możesz dziś napisać to bardziej zwięźle jako [...].filter(x => x==2).length. Jeśli zależy Ci na wydajności, pamiętaj, że chociaż jest to asymptotycznie taka sama wydajność jak pętla for (czas O (N)), może wymagać dodatkowej pamięci O (N) (zamiast pamięci O (1)), ponieważ będzie to prawie z pewnością wygeneruj tablicę pośrednią, a następnie policz elementy tej tablicy pośredniej.

ninjagecko
źródło
1
Jest to dobre rozwiązanie FP, jedyny „problem” (nieistotny w większości przypadków) tworzy pośrednią tablicę.
tokland
1
@tokland: Jeśli to jest problemem, możesz to zrobićarray.reduce(function(total,x){return x==value? : total+1 : total}, 0)
ninjagecko
1
@ninjagecko Czy operator trójskładnikowy nie powinien zawierać tylko jednego dwukropka? [...].reduce(function(total,x){return x==2 ? total+1 : total}, 0)
A.Krueger
2
@tokland Może filtr nie utworzy pośredniej tablicy. Dobry kompilator optymalizujący może łatwo rozpoznać, że używana jest tylko długość tablicy. Może żaden z obecnych kompilatorów JS nie jest wystarczająco inteligentny, aby to zrobić, ale to nie jest ważne. Jak mówi Fowler: „Jeśli coś boli, zrób więcej”. Pisanie złego kodu jest krótkowzroczne, aby uniknąć niedociągnięć kompilatora. Jeśli kompilator jest do niczego, napraw kompilator. mlafeldt.github.io/blog/if-it-hurts-do-it-more-often
John Henckel
Uważam to rozwiązanie optymalne 2017: const count = (list) => list.filter((x) => x == 2).length. Następnie użyj go, dzwoniąc, count(list)gdzie lista jest tablicą liczb. Możesz także const count = (list) => list.filter((x) => x.someProp === 'crazyValue').lengthpoliczyć wystąpienia crazyValue w tablicy obiektów. Uwaga, jest to dokładne dopasowanie do właściwości.
agm1984,
71

Aktualizacja ES6 do JS:

Pamiętaj, że zawsze powinieneś używać potrójnych równości: ===aby uzyskać prawidłowe porównanie:

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

Następująca jednomyślna funkcja Arrow (funkcja lambda) w JS:

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

można uprościć do tego zwięzłego formularza dla pojedynczego wejścia:

x => 2 * x

gdzie returnjest domniemane.

Sverrisson
źródło
Czy funkcja filtra jest bardziej wydajna niż użycie pętli es6 for?
Niklas
1
@Niklas, myślę, że to to samo (obaj muszą sprawdzić wszystkie elementy, O (N)), ale myślę, że zależy to od przeglądarki, a także od liczby elementów i komputera, co mieści się w pamięci podręcznej. Myślę, że odpowiedź brzmi: „To skomplikowane” :)
Sverrisson
67

2017: Jeśli ktoś nadal jest zainteresowany pytaniem, moje rozwiązanie jest następujące:

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

Raild
źródło
8

Jeśli używasz lodash lub podkreślenia, metoda _.countBy zapewni obiekt zagregowanych sum wpisanych przez każdą wartość w tablicy. Możesz zmienić to w jednolinijkowy, jeśli chcesz policzyć tylko jedną wartość:

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

Działa to również dobrze w przypadku tablic liczb. Jednowierszowy przykład będzie wyglądał następująco:

_.countBy([1, 2, 3, 5, 2, 8, 9, 2])[2]; // 3
Coleman
źródło
5
Ogromna przesada. Ponieważ tworzy liczniki dla wszystkich unikalnych elementów. Zmarnowane miejsce i czas.
metalim
5

Najdziwniejszy sposób, w jaki mogę to zrobić, to:

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

Gdzie:

  • a jest tablicą
  • n to liczba do zliczenia w tablicy

Moja sugestia, wykorzystaj chwilę lub pętlę ;-)

Gary Green
źródło
3

Brak pętli oznacza zwykle przekazanie procesu do jakiejś metody, która korzysta z pętli.

Oto sposób, w jaki nasz programista, który nienawidzi pętli, może zaspokoić swoją nienawiść za pewną cenę:

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

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


/*  returned value: (Number)
3
*/

Możesz również wielokrotnie wywoływać indexOf, jeśli jest dostępna jako metoda tablicowa, i za każdym razem przesuwać wskaźnik wyszukiwania.

Nie tworzy to nowej tablicy, a pętla jest szybsza niż forEach lub filter.

Jeśli masz milion członków, na których możesz spojrzeć, może to mieć znaczenie.

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
*/
kennebec
źródło
2
Możesz zredukować swoje wyrażenie regularne do just String(a).match(/2/g).length + 1- chociaż uważaj na to, bo inaczej Twoja implementacja nie będzie dobrze działać z dwucyfrowymi liczbami.
Gary Green,
2
a co z [2, 22, 2]?
Oduvan
2

Większość opublikowanych rozwiązań wykorzystujących funkcje tablicowe, takie jak filtr, jest niekompletna, ponieważ nie są sparametryzowane.

Oto rozwiązanie, za pomocą którego element do zliczania można ustawić w czasie wykonywania.

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

Zaletą tego podejścia jest to, że można łatwo zmienić funkcję, aby policzyć na przykład liczbę elementów większą niż X.

Możesz również zadeklarować funkcję redukcji w tekście

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);
Luis Orantes
źródło
var elementToFind=2; ... function (elementToFind, total, number){ return total += number==elementToFind; }.bind(this, elementToFind) ...jest trudniejszy do odczytania i nie daje żadnej przewagi nad sprawiedliwymi ... (acc, x) => acc += number == 2.... Podoba mi się, że używasz +=zamiast acc + (number == 2)tego. Czuje się jednak jak nieuzasadnione hakowanie składni.
masterxilo
2

Naprawdę, dlaczego trzeba mapalbo filterdo tego? reduce„urodził się” dla tego rodzaju operacji:

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

Otóż ​​to! (jeśli item==valw każdej iteracji, to 1 zostanie dodany do akumulatora count, zgodnie truez rozwiązaniem 1).

Jako funkcja:

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

Lub śmiało rozszerz swoje tablice:

Array.prototype.count = function(val) {
   return this.reduce((count,item)=>count+(item==val),0)
}
Yuval A.
źródło
2

Lepiej jest opakować go w funkcję:

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

countNumber([1,2,3,4,5],3) // returns 1
Justin Herrera
źródło
2

Oto sposób ES2017 +, aby uzyskać zliczenia dla wszystkich elementów tablicy w 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;
});

Możesz również opcjonalnie posortować dane wyjściowe:

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

console.log (countsSorted) dla Twojej przykładowej tablicy:

[
  [ '2', 3 ],
  [ '1', 1 ],
  [ '3', 1 ],
  [ '5', 1 ],
  [ '8', 1 ],
  [ '9', 1 ]
]
Yanick J. Steinbeck
źródło
1

Wierzę, że szukasz funkcjonalnego podejścia

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

elem === „a” to warunek, zastąp go własnym.

Shantanu Bhadoria
źródło
Nie wypisze 3, ale 0. Aby to naprawić, druga linia powinna być count = arr.filter(elem => elem === 'a').lengthlubcount = arr.filter(elem => {return elem === 'a'}).length
WPomier
1

Jestem początkującym fanem funkcji redukującej js array.

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

W rzeczywistości, jeśli naprawdę chcesz się spodobać, możesz utworzyć funkcję zliczającą w prototypie Array. Następnie możesz go użyć ponownie.

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

Więc zrób

const myArray =[1, 2, 3, 5, 2, 8, 9, 2]
const count = myArray.count(x => x==2)
Scott Blanch
źródło
0

Rozwiązanie przez rekurencję

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
Giffo
źródło
1
Czy to obsługuje pustą tablicę?
Andrew Grimm
@AndrewGrimm ma rację. Podstawowy przypadek to arr.length == 0
Justin Meiners
Niezłe rozwiązanie! Próbowałem zrobić coś, używając rekursji tylko dla ćwiczeń, a twój przykład był bardziej elegancki niż to, co robiłem. To zdecydowanie bardziej skomplikowany sposób niż przy użyciu filter, reducelub prostyforLoop , a także droższe patrząc na wydajność, ale nadal świetny sposób to zrobić z rekursji. Moją jedyną zmianą jest: po prostu myślę, że byłoby lepiej utworzyć funkcję i dodać do niej filtr, aby skopiować tablicę i uniknąć mutacji oryginalnej tablicy, a następnie użyć funkcji rekurencyjnej jako funkcji wewnętrznej.
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');
}

Zrobiłbym coś takiego:

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

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

  console.log(dd);
}

roni
źródło
0

Utwórz nową metodę dla klasy Array w pliku poziomu podstawowego i używaj jej w całym projekcie.

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

Użyj tego w dowolnym miejscu w swoim projekcie -

[1, 2, 4, 5, 2, 7, 2, 9].occurrence(2);
// above line returns 3
Rushikesh Bharad
źródło
0

Oto jedna linijka w javascript.

  1. Użyj mapy. Znajdź pasujące wartości(v === 2) w tablicy, zwracając tablicę jedynek i zer.
  2. Użyj Reduce. Dodaj wszystkie wartości tablicy dla łącznej liczby znalezionych.
[1, 2, 3, 5, 2, 8, 9, 2]
  .map(function(v) {
    return v === 2 ? 1 : 0;
  })
  .reduce((a, b) => a + b, 0);

Wynik jest 3.

Jake
źródło
0

W zależności od tego, jak chcesz go uruchomić:

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, …}
Mohammad Hassan
źródło
-7

Możesz użyć właściwości length w tablicy JavaScript:

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

myarray = [1,2];
count = myarray.length;//return 2
Bhavik Bhavsar
źródło
Jeśli najpierw ją przefiltrujesz, możesz użyć długości. ie array.filter (x => x === 2) .length
Scott Blanch