Bagaimana cara menghitung elemen tertentu dalam array?

162

Saya memiliki sebuah array:

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

Saya ingin tahu berapa banyak 2yang ada dalam array.

Apa cara paling elegan untuk melakukannya dalam JavaScript tanpa mengulang dengan forloop?

Leem
sumber

Jawaban:

90

Sangat sederhana:

var count = 0;
for(var i = 0; i < array.length; ++i){
    if(array[i] == 2)
        count++;
}
Thor Jacobsen
sumber
53
Tidak, yang saya maksud adalah tanpa mengulang dengan "untuk"
Leem
9
@Leem: Mengapa perulangan buruk? Selalu ada perulangan di beberapa titik. Jelas Anda akan membuat fungsi yang menyembunyikan loop. Ini "Saya tidak ingin menggunakan alat yang tepat untuk pekerjaan itu" - pertanyaan tidak pernah masuk akal bagi saya. Dan kita bisa berdebat apa yang paling elegan. Misalnya bagi saya, membuat panggilan fungsi per elemen hanya untuk membandingkannya dengan nilai tidak elegan.
Felix Kling
2
untuk tertawa: lansiran (eval ('(' + my_array.join ('== 2) + (') + '== 2)')) jsfiddle.net/gaby_de_wilde/gujbmych
user40521
29
OP mungkin berpikir perulangan itu buruk karena merupakan 5 baris kode dan memerlukan status yang bisa berubah. Pengembang yang datang untuk membacanya nanti harus meluangkan waktu untuk memeriksa apa fungsinya, dan kehilangan fokus dari tugas mereka. Sebuah abstraksi jauh lebih unggul: const count = countItems(array, 2);dan detail implementasi dapat diperdebatkan di dalamnya.
joeytwiddle
2
Ini bukan jawaban yang tepat karena pertanyaannya jelas meminta untuk tidak menggunakan loop. Periksa solusi saya yang tidak menggunakan loop. stackoverflow.com/a/44743436/8211014
Luis Orantes
289

[ jawaban ini sedikit bertanggal: baca hasil edit ]

Katakan halo kepada teman-teman Anda: mapdan filterdan reducedan forEachdan everysebagainya.

(Saya hanya sesekali menulis for-loop dalam javascript, karena pelingkupan tingkat blok tidak ada, jadi Anda harus tetap menggunakan fungsi sebagai badan loop jika Anda perlu menangkap atau mengkloning indeks atau nilai iterasi Anda. For-loop lebih efisien secara umum, tetapi kadang-kadang Anda perlu penutupan.)

Cara yang paling mudah dibaca:

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

(Kita bisa menulis .filter(function(x){return x==2}).lengthsebagai gantinya)

Berikut ini lebih hemat ruang (O (1) daripada O (N)), tapi saya tidak yakin berapa banyak manfaat / penalti yang mungkin Anda bayarkan dalam hal waktu (tidak lebih dari faktor konstan sejak Anda mengunjungi setiap elemen tepat sekali):

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

(Jika Anda perlu mengoptimalkan bagian kode ini, perulangan for mungkin lebih cepat di beberapa peramban ... Anda dapat menguji berbagai hal di jsperf.com.)


Anda kemudian dapat menjadi elegan dan mengubahnya menjadi fungsi prototipe:

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

Seperti ini:

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

Anda juga dapat menempel teknik for-loop lama yang biasa (lihat jawaban lain) di dalam definisi properti di atas (sekali lagi, itu kemungkinan akan jauh lebih cepat).


Sunting 2017 :

Aduh, jawaban ini lebih populer daripada jawaban yang benar. Sebenarnya, gunakan saja jawaban yang diterima. Sementara jawaban ini mungkin lucu, kompiler js mungkin tidak (atau tidak bisa karena spek) mengoptimalkan kasus tersebut. Jadi, Anda harus benar-benar menulis simpel untuk loop:

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

Anda dapat menentukan versi .countStrictEq(...)yang menggunakan ===gagasan persamaan. Gagasan kesetaraan mungkin penting untuk apa yang Anda lakukan! (misalnya [1,10,3,'10'].count(10)==2, karena angka-angka seperti '4' == 4 dalam javascript ... maka memanggilnya .countEqatau .countNonstrictmenekankan itu menggunakan ==operator.)

Juga pertimbangkan untuk menggunakan struktur data multiset Anda sendiri (misalnya seperti python ' collections.Counter') untuk menghindari keharusan melakukan penghitungan di tempat pertama.

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: Meskipun, jika Anda masih menginginkan cara pemrograman fungsional (atau pelepasan one-liner tanpa mengesampingkan Array.prototype), Anda dapat menuliskannya dengan lebih singkat saat ini sebagai [...].filter(x => x==2).length. Jika Anda peduli dengan kinerja, perhatikan bahwa sementara ini asimptotik kinerja yang sama dengan waktu for-loop (O (N)), mungkin memerlukan O (N) memori tambahan (bukan memori O (1)) karena akan hampir tentu menghasilkan array perantara dan kemudian menghitung elemen-elemen array menengah itu.

ninjagecko
sumber
1
Ini adalah solusi FP yang baik, satu-satunya "masalah" (tidak relevan untuk kebanyakan kasus) itu menciptakan array perantara.
tokland
1
@tokland: Jika itu masalah, Anda bisa melakukannyaarray.reduce(function(total,x){return x==value? : total+1 : total}, 0)
ninjagecko
1
@ninjagecko Bukankah seharusnya hanya ada satu titik dua di operator ternary? [...].reduce(function(total,x){return x==2 ? total+1 : total}, 0)
A.Krueger
2
@tokland Mungkin filter tidak akan membuat array perantara. Kompiler pengoptimalisasi yang baik dapat dengan mudah mengenali bahwa hanya panjang array yang digunakan. Mungkin tidak ada kompiler JS saat ini yang cukup pintar untuk melakukan ini, tetapi itu tidak penting. Seperti yang dikatakan Fowler, "Jika ada sesuatu yang menyakitkan, maka lakukan lebih dari itu". Ini adalah pandangan pendek untuk menghindari kekurangan kompiler dengan menulis kode yang buruk. Jika kompiler menyebalkan, perbaiki kompiler. mlafeldt.github.io/blog/if-it-hurts-do-it-more-often
John Henckel
Saya akan mempertimbangkan ini solusi yang optimal 2017: const count = (list) => list.filter((x) => x == 2).length. Kemudian gunakan dengan memanggil di count(list)mana daftar adalah array angka. Anda juga bisa melakukan const count = (list) => list.filter((x) => x.someProp === 'crazyValue').lengthuntuk menghitung instance crazyValue dalam array objek. Catatan, ini adalah pencocokan tepat untuk properti.
agm1984
71

ES6 Pembaruan ke JS:

Perhatikan bahwa Anda harus selalu menggunakan triple sama dengan: ===untuk mendapatkan perbandingan yang benar:

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

Fungsi Panah dengan suara bulat berikut (fungsi lambda) di JS:

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

dapat disederhanakan ke formulir ringkas ini untuk satu input:

x => 2 * x

dimana returntersirat.

Sverrisson
sumber
Apakah fungsi filter lebih berkinerja daripada menggunakan es6 untuk loop?
Niklas
1
@ Niklas, saya pikir itu sama (karena keduanya harus memeriksa semua elemen, O (N)), tapi, saya kira itu tergantung pada browser dan juga pada jumlah elemen dan komputer tentang apa yang cocok dalam memori cache. Jadi, saya kira jawabannya adalah: "Ini rumit" :)
Sverrisson
67

2017: Jika seseorang masih tertarik dengan pertanyaan itu, solusi saya adalah sebagai berikut:

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
sumber
8

Jika Anda menggunakan lodash atau garis bawah metode _.countBy akan memberikan objek total agregat yang dikunci oleh setiap nilai dalam array. Anda bisa mengubahnya menjadi satu-liner jika Anda hanya perlu menghitung satu nilai:

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

Ini juga berfungsi dengan baik pada array angka. Satu-liner untuk contoh Anda adalah:

_.countBy([1, 2, 3, 5, 2, 8, 9, 2])[2]; // 3
Koleman
sumber
5
Pembunuhan besar-besaran. Saat itu membuat penghitung untuk semua elemen unik. Penyimpanan dan waktu yang terbuang.
metalim
5

Cara teraneh yang bisa saya pikirkan untuk melakukan ini adalah:

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

Dimana:

  • a adalah array
  • n adalah angka untuk dihitung dalam array

Saran saya, gunakan sebentar atau untuk loop ;-)

Gary Green
sumber
3

Tidak menggunakan loop biasanya berarti menyerahkan proses ke beberapa metode yang tidak menggunakan loop.

Inilah cara loop kita yang membenci coder dapat memuaskan kebenciannya, dengan harga:

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

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


/*  returned value: (Number)
3
*/

Anda juga dapat berulang kali memanggil indexOf, jika tersedia sebagai metode array, dan memindahkan pointer pencarian setiap kali.

Ini tidak membuat array baru, dan loop lebih cepat dari forEach atau filter.

Ini bisa membuat perbedaan jika Anda memiliki sejuta anggota untuk melihatnya.

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
sumber
2
Anda dapat mengurangi regex menjadi hanya String(a).match(/2/g).length + 1- meskipun waspadai ini atau implementasi Anda tidak akan bermain bagus dengan dua digit
Gary Green
2
bagaimana dengan [2, 22, 2]?
Oduvan
2

Sebagian besar solusi yang diposting menggunakan fungsi array seperti filter tidak lengkap karena tidak parameter.

Ini dia solusi untuk menghitung elemen yang bisa diatur pada saat run time.

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

Keuntungan dari pendekatan ini adalah dapat dengan mudah mengubah fungsi untuk menghitung misalnya jumlah elemen lebih besar dari X.

Anda juga dapat mendeklarasikan fungsi pengurangan sebaris

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
sumber
var elementToFind=2; ... function (elementToFind, total, number){ return total += number==elementToFind; }.bind(this, elementToFind) ...lebih sulit untuk dibaca dan tidak memberikan keuntungan lebih dari adil ... (acc, x) => acc += number == 2.... Saya suka penggunaan Anda +=alih-alih acc + (number == 2). Terasa seperti sintaks yang tidak beralasan HACK.
masterxilo
2

Sungguh, mengapa Anda perlu mapatau filteruntuk ini? reduce"dilahirkan" untuk operasi semacam ini:

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

itu dia! (jika item==valdalam setiap iterasi, maka 1 akan ditambahkan ke akumulator count, seperti yang trueakan diselesaikan 1).

Sebagai fungsi:

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

Atau, lanjutkan dan rentangkan array Anda:

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

Lebih baik membungkusnya menjadi fungsi:

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

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

Berikut adalah cara ES2017 + untuk mendapatkan jumlah semua item array di 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;
});

Anda juga dapat secara opsional mengurutkan output:

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

console.log (countsSorted) untuk contoh array Anda:

[
  [ '2', 3 ],
  [ '1', 1 ],
  [ '3', 1 ],
  [ '5', 1 ],
  [ '8', 1 ],
  [ '9', 1 ]
]
Yanick J. Steinbeck
sumber
1

Saya percaya apa yang Anda cari adalah pendekatan fungsional

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

elem === 'a' adalah syaratnya, ganti dengan milikmu.

Shantanu Bhadoria
sumber
Ini tidak akan mencetak 3, tetapi 0. Untuk memperbaikinya, baris kedua Anda harus count = arr.filter(elem => elem === 'a').lengthataucount = arr.filter(elem => {return elem === 'a'}).length
WPomier
1

Saya penggemar berat fungsi js array.

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

Bahkan jika Anda benar-benar ingin menjadi mewah, Anda dapat membuat fungsi hitungan pada prototipe Array. Maka Anda dapat menggunakannya kembali.

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

Lalu lakukan

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

Solusi dengan rekursi

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
sumber
1
Apakah ini menangani array kosong?
Andrew Grimm
@AndrewGrimm benar. Kasing dasar adalah arr.length == 0
Justin Meiners
Solusi bagus! Saya mencoba melakukan sesuatu menggunakan rekursi hanya untuk latihan dan teladan Anda lebih elegan dari apa yang saya lakukan. Ini jelas cara yang lebih kompleks daripada menggunakan filter, reduceatau yang sederhana forLoop, dan juga, lebih mahal ketika melihat kinerja, tetapi masih cara yang bagus untuk melakukannya dengan rekursi. Satu-satunya perubahan saya adalah: Saya hanya berpikir akan lebih baik untuk membuat fungsi dan menambahkan filter di dalamnya untuk menyalin array dan menghindari mutasi dari array asli, kemudian menggunakan rekursif sebagai fungsi dalam.
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');
}

Saya akan melakukan sesuatu seperti itu:

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

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

  console.log(dd);
}

roni
sumber
0

Buat metode baru untuk kelas Array dalam file tingkat inti dan gunakan di seluruh proyek Anda.

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

Gunakan ini di mana saja dalam proyek Anda -

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

Berikut ini adalah satu liner di javascript.

  1. Gunakan peta. Temukan nilai yang cocok(v === 2) dalam array, kembalikan array yang dan nol.
  2. Gunakan Reduce. Tambahkan semua nilai array untuk jumlah total yang ditemukan.
[1, 2, 3, 5, 2, 8, 9, 2]
  .map(function(v) {
    return v === 2 ? 1 : 0;
  })
  .reduce((a, b) => a + b, 0);

Hasilnya adalah 3.

Jake
sumber
0

Bergantung pada bagaimana Anda ingin menjalankannya:

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

Anda dapat menggunakan properti length dalam array JavaScript:

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

myarray = [1,2];
count = myarray.length;//return 2
Bhavik Bhavsar
sumber
Jika Anda memfilternya terlebih dahulu maka Anda bisa menggunakan panjangnya. yaitu array.filter (x => x === 2) .length
Scott Blanch