Dizideki belirli öğeleri nasıl sayabilirim?


162

Bir dizi var:

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

2Dizide kaç tane s olduğunu bilmek istiyorum .

Döngü ile fordöngü yapmadan JavaScript'te yapmanın en zarif yolu nedir ?

Yanıtlar:


90

Çok basit:

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

53
Hayır, demek istediğim "for" ile
döngüsüz

9
@Leem: Döngü neden kötü? Her zaman bir noktada döngü vardır. Açıkçası, döngüyü gizleyen bir işlev yaratabilirsiniz. Bu "iş için doğru aracı kullanmak istemiyorum" - talepler bana hiç mantıklı gelmedi. Ve en zarif olanı tartışabiliriz. Örneğin benim için, sadece bir değerle karşılaştırmak için eleman başına bir işlev çağrısı yapmak zarif değildir.
Felix Kling

2
gülüşler için: alert (eval ('(' + my_array.join ('== 2) + (') + '== 2)')) jsfiddle.net/gaby_de_wilde/gujbmych
user40521

29
OP muhtemelen 5 satır kod olduğu ve değişebilir durum gerektirdiği için döngünün kötü olduğunu düşünüyor. Daha sonra okumak için gelen bir geliştirici, ne yaptığını kontrol etmek ve görevlerinden odaklanmak için biraz zaman harcamak zorunda kalacak. Bir soyutlama çok daha üstündür: const count = countItems(array, 2);ve uygulama ayrıntıları içeride tartışılabilir.
joeytwiddle

2
Bu doğru cevap değil çünkü soru açıkça döngüler kullanmamanızı istiyor. Döngülerden faydalanmayan çözümümü kontrol et. stackoverflow.com/a/44743436/8211014
Luis Orantes

289

[ bu cevap biraz tarihli: düzenlemeleri okuyun ]

Arkadaşlarına merhaba de: mapvefilter ve reduceve forEachve everyvb

(Sadece bazen javascript'te for-loop'ları yazıyorum, çünkü blok seviyesi kapsamı eksiktir, bu nedenle yineleme indeksinizi veya değerinizi yakalamanız veya klonlamanız gerekiyorsa, zaten döngü gövdesi olarak bir işlev kullanmanız gerekir. genel olarak daha verimlidir, ancak bazen bir kapatmaya ihtiyacınız vardır.)

En okunabilir yol:

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

( .filter(function(x){return x==2}).lengthBunun yerine yazabilirdik)

Aşağıdakiler, O (N) yerine daha fazla yer tasarrufu sağlar (O (1)), ancak zaman açısından ne kadar fayda / ceza ödeyebileceğinizden emin değilim (ziyaret ettiğinizden beri sabit bir faktörden fazla değil) her öğe tam olarak bir kez):

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

(Bu özel kod parçasını optimize etmeniz gerekirse, bazı tarayıcılarda bir for döngüsü daha hızlı olabilir ... jsperf.com'da bir şeyler test edebilirsiniz.)


Daha sonra zarif olabilir ve bir prototip işlevine dönüştürebilirsiniz:

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

Bunun gibi:

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

Yukarıdaki özellik tanımının içine normal eski for-loop tekniğini de yapıştırabilirsiniz (diğer yanıtlara bakın) (yine, muhtemelen daha hızlı olacaktır).


2017 düzenleme :

Hata! Bu cevap doğru cevaptan daha popüler hale geldi. Aslında, sadece kabul edilen cevabı kullanın. Bu cevap sevimli olsa da, js derleyicileri muhtemelen bu gibi durumları optimize etmez (veya spec nedeniyle yapamaz). Yani gerçekten basit bir döngü yazmalısınız:

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

Eşitlik kavramını .countStrictEq(...)kullanan bir sürüm tanımlayabilirsiniz ===. Eşitlik kavramı yaptığınız şey için önemli olabilir! (örneğin [1,10,3,'10'].count(10)==2, javascript'teki '4' == 4 gibi sayılar ... dolayısıyla onu çağırır .countEqveya operatörü .countNonstrictkullanır ==.)

Ayrıca collections.Counter, sayımı ilk etapta yapmaktan kaçınmak için kendi çoklu set veri yapınızı (örneğin, python ' ' gibi) kullanmayı düşünün .

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: Yine de, hala işlevsel programlama yolunu (veya Array.prototype'i geçersiz kılmadan tek bir tek satırlık) istiyorsanız, bugünlerde daha tersine yazabilirsiniz [...].filter(x => x==2).length. Performansı önemsiyorsanız, bunun döngüsel olmayan (O (N) zaman) ile asimptotik olarak aynı performans olmasına rağmen, O (N) ekstra bellek (O (1) bellek yerine) gerektirebileceğine dikkat edin. kesinlikle bir ara dizi oluşturun ve daha sonra o ara dizinin elemanlarını sayın.


1
Bu iyi bir FP çözümü, tek "sorun" (çoğu durumda ilgisiz) bir ara dizi oluşturur.
tokland

1
@tokland: Bu bir endişeniz varsa, şunları yapabilirsinizarray.reduce(function(total,x){return x==value? : total+1 : total}, 0)
ninjagecko

1
@ninjagecko Üçlü operatörde yalnızca bir kolon olmamalı mı? [...].reduce(function(total,x){return x==2 ? total+1 : total}, 0)
A.Krueger

2
@tokland Belki filtre edecek olmayan bir ara dizi oluşturmak. İyi bir optimize edici derleyici, sadece dizinin uzunluğunun kullanıldığını kolayca tanıyabilir. Belki şu anki JS derleyicilerinin hiçbiri bunu yapacak kadar akıllı değildir, ancak bu önemli değildir. Fowler'in dediği gibi, "Bir şey acıyorsa, daha fazlasını yapın". Kötü kod yazarak derleyici eksikliklerini önlemek için kısa görüş vardır. Derleyici emilirse, derleyiciyi sabitleyin. mlafeldt.github.io/blog/if-it-hurts-do-it-more-often
John Henckel

Bu optimal 2017 çözümü ele alacak: const count = (list) => list.filter((x) => x == 2).length. Ardından count(list), listenin bir sayı dizisi olduğunu çağırarak kullanın . const count = (list) => list.filter((x) => x.someProp === 'crazyValue').lengthNesne dizisindeki crazyValue örneklerini saymak için de yapabilirsiniz . Not, bu özellik için tam bir eşleşme.
agm1984

71

ES6 JS Güncellemesi:

===Doğru karşılaştırmayı elde etmek için daima üçlü eşitler kullanmanız gerektiğini unutmayın :

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

JS'de şu oybirliğiyle Ok işlevi (lambda işlevi):

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

tek bir giriş için bu kısa forma basitleştirilebilir:

x => 2 * x

returnzımni olduğu yerde .


Filtre işlevi, döngü için es6 kullanmaktan daha mı başarılı?
Niklas

1
@Niklas, sanırım aynı (her ikisi de tüm öğeleri kontrol etmek zorunda olduğu gibi, O (N)), ama, sanırım tarayıcıya bağımlı ve ayrıca önbellekte neyin uyduğu ile ilgili öğe ve bilgisayar sayısına bağlı. Yani, sanırım cevap: "Bu karmaşık" :)
Sverrisson

67

2017: Birisi hala soruyla ilgileniyorsa, çözümüm şudur:

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

Lodash veya alt çizgi kullanıyorsanız, _.countBy yöntemi, dizideki her değerle anahtarlanan toplamların bir nesnesini sağlar. Yalnızca bir değeri saymanız gerekiyorsa, bunu bir astara dönüştürebilirsiniz:

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

Bu aynı zamanda sayı dizilerinde de işe yarar. Örneğinizin tek astarı şöyle olacaktır:

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

5
Büyük bir taşma. Tüm benzersiz elemanlar için sayaçlar oluşturduğu için. Boşa giden depolama ve zaman.
metalim

5

Bunu yapmanın en tuhaf yolu:

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

Nerede:

  • a dizidir
  • n dizide sayılacak sayıdır

Benim önerim, bir süre veya döngü için kullanın ;-)


3

Bir döngü kullanmama genellikle bir yönteme çok süreci teslim anlamına gelmez bir döngü kullanılır.

İşte döngü nefret kodlayıcımızın nefretini bir fiyatla tatmin edebilmesinin bir yolu:

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

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


/*  returned value: (Number)
3
*/

Dizi yöntemi olarak kullanılabiliyorsa, indexOf işlevini art arda çağırabilir ve her seferinde arama işaretçisini taşıyabilirsiniz.

Bu yeni bir dizi oluşturmaz ve döngü forEach veya filtreden daha hızlıdır.

Bakmak için bir milyon üyeniz varsa bu bir fark yaratabilir.

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
Normal ifadenizi sadece azaltabilirsiniz String(a).match(/2/g).length + 1- ancak buna dikkat edin veya uygulamanız çift haneli rakamlarla iyi oynamaz.
Gary Green

2
[2, 22, 2] hakkında ne dersiniz?
Oduvan

2

Filtre gibi dizi işlevlerini kullanan gönderilen çözümlerin çoğu, parametreleştirilmediğinden eksiktir.

Burada sayılacak öğenin çalışma zamanında ayarlanabileceği bir çözüm gider.

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

Bu yaklaşımın avantajı, örneğin X'den büyük elemanların sayısını saymak için işlevi kolayca değiştirebilmesidir.

Ayrıca azaltma işlevini satır içinde de bildirebilirsiniz

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) ...okunması daha zordur ve sadece avantaj sağlamaz ... (acc, x) => acc += number == 2.... Ben kullanımınızı gibi +=yerine acc + (number == 2)gerçi. Gerçi bir haksız sözdizimi HACK gibi geliyor.
masterxilo

2

Gerçekten, neden gerekir mapveya filterbunun için? reducebu tür operasyonlar için "doğdu":

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

bu kadar! ( item==valher yinelemede, çözüleceği countgibi akümülatöre 1 eklenecektir ).true1

İşlev olarak:

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

Veya devam edin ve dizilerinizi genişletin:

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

2

Fonksiyona sarmak daha iyidir:

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

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

2

O (N) 'deki tüm dizi öğelerinin sayısını almanın bir ES2017 + yolu:

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

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

İsteğe bağlı olarak çıktıyı da sıralayabilirsiniz:

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

Örnek diziniz için console.log (countsSorted):

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

1

Aradığın şeyin fonksiyonel yaklaşım olduğuna inanıyorum

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

elem === 'a' koşulu, kendinizinkini değiştirin.


3 yazdırmaz, ancak 0 count = arr.filter(elem => elem === 'a').lengthcount = arr.filter(elem => {return elem === 'a'}).length
düzelmez

1

Ben bir js dizisinin azaltma işlevinin hayranıyım.

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

Aslında gerçekten süslemek istiyorsanız, Array prototipinde bir sayım işlevi oluşturabilirsiniz. Sonra tekrar kullanabilirsiniz.

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

Sonra yap

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

0

Özyineleme ile çözüm

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
Bu boş bir diziyi ele alıyor mu?
Andrew Grimm

@AndrewGrimm doğru. Temel dava arr.length == 0
Justin Meiners

Güzel çözüm! Sadece pratik için özyineleme kullanarak bir şeyler yapmaya çalışıyordum ve örneğim yaptığımdan daha zarifti. Performansa bakarken kesinlikle kullanmaktan daha karmaşık bir yol filter, reduceya da basit forLoopve aynı zamanda daha pahalı, ancak yine de özyineleme ile yapmanın harika bir yolu. Benim tek değişiklik: Ben sadece bir işlev oluşturmak ve dizi kopyalamak ve orijinal dizinin bir mutasyon önlemek için içine bir filtre eklemek için daha iyi olacağını düşünüyorum, sonra özyinelemeli bir iç işlev olarak kullanın.
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');
}

Böyle bir şey yapardım:

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

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

  console.log(dd);
}


0

Temel düzey dosyasında Array sınıfı için yeni bir yöntem oluşturun ve bunu projenizin her yerinde kullanın.

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

Bunu projenizde herhangi bir yerde kullanın -

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

0

Javascript'te bir astar var.

  1. Haritayı kullan. (v === 2)Bir dizi ve sıfırlar dizisi döndürerek dizideki eşleşen değerleri bulun .
  2. Azalt'ı kullanın. Bulunan toplam sayı için dizinin tüm değerlerini ekleyin.
[1, 2, 3, 5, 2, 8, 9, 2]
  .map(function(v) {
    return v === 2 ? 1 : 0;
  })
  .reduce((a, b) => a + b, 0);

Sonuç 3.


0

Nasıl çalıştırmak istediğinize bağlı olarak:

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

Uzunluk özelliğini JavaScript dizisinde kullanabilirsiniz:

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

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

Önce filtrelerseniz, uzunluğu kullanabilirsiniz. dizi.filtre (x => x === 2). uzunluk
Scott Blanch
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.