配列内の特定の要素を数える方法は?


161

私は配列を持っています:

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

2配列にがいくつあるか知りたいのですが。

ループでforループせずにJavaScriptでそれを行う最もエレガントな方法は何ですか?

回答:


90

非常にシンプル:

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

53
いいえ、つまり「for」でループしないことを意味します
Leem

9
@リーム:ループが悪いのはなぜですか?ある時点で常にループが発生します。明らかに、ループを隠す関数を作成します。これらの「仕事に適したツールを使いたくない」 -リクエストは私にはあまり意味がありませんでした。そして、最もエレガントなものを議論することができます。たとえば、私にとっては、要素ごとに関数を呼び出して値と比較するだけではエレガントではありません。
Felix Kling、

2
笑いの場合:alert(eval( '(' + my_array.join( '== 2)+(')+ '== 2)')) jsfiddle.net/gaby_de_wilde/gujbmych
user40521

29
5行のコードであり、変更可能な状態を必要とするため、OPはおそらくループが悪いと考えています。後でそれを読んだ開発者は、それが何をしているかを確認するためにいくらかの時間を費やさなければならず、彼らの仕事から集中力を失うでしょう。抽象化ははるかに優れてconst count = countItems(array, 2);おり、実装の詳細は内部で議論することができます。
joeytwiddle

2
質問はループを使用しないことを明確に要求するため、これは正しい答えではありません。ループを使用しないソリューションを確認してください。stackoverflow.com/a/44743436/8211014
Luis Orantes 2018

287

[ この答えは少し日付が古い:編集を読む ]

友達に挨拶する:mapand filterand reduceand forEachand everyetc.

(ブロックレベルのスコープが欠落しているため、JavaScriptでたまにforループを書くだけなので、反復インデックスまたは値をキャプチャまたは複製する必要がある場合は、とにかく関数をループの本体として使用する必要があります。Forループ一般的にはより効率的ですが、場合によってはクロージャーが必要になります。)

最も読みやすい方法:

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

.filter(function(x){return x==2}).length代わりに書くこともできました)

以下はよりスペース効率が良い(O(N)ではなくO(1))ですが、時間に関してどの程度のメリット/ペナルティを支払うことができるかはわかりません(アクセスしてから一定の係数を超えないでください)各要素を1回だけ):

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

(この特定のコードを最適化する必要がある場合、一部のブラウザーではforループの方が高速かもしれません... jsperf.comでテストできます。)


その後、エレガントにしてプロトタイプ関数に変えることができます。

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

このような:

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

また、上記のプロパティ定義内に、通常の古いforループテクニック(他の回答を参照)を挿入することもできます(ここでも、はるかに高速です)。


2017編集

おっと、この答えは正解よりも人気が高まっています。実際には、受け入れられた答えを使用してください。この答えはかわいいかもしれませんが、jsコンパイラーはおそらくそのようなケースを最適化しません(または仕様のために最適化できません)。だからあなたは本当に単純な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;
        }
    }
});

平等.countStrictEq(...)===概念を使用したバージョンを定義できます。平等の概念はあなたがしていることにとって重要かもしれません!(たとえば[1,10,3,'10'].count(10)==2、JavaScriptの「4」== 4のような数値...それを呼び出す.countEq.countNonstrict==演算子を使用することを強調します。)

またcollections.Counter、最初にカウントを行う必要がないように、独自のマルチセットデータ構造(たとえば、pythonの ' 'のような)を使用することも検討してください。

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

デモ:

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

補足:ただし、関数型プログラミングの方法(またはArray.prototypeをオーバーライドせずに使い捨てのワンライナー)が必要な場合は、最近ではもっと簡潔に記述できます[...].filter(x => x==2).length。パフォーマンスを重視する場合、これはfor-loop(O(N)時間)と漸近的に同じパフォーマンスですが、ほぼO(1)メモリではなくO(N)メモリが必要になる場合があることに注意してください。確かに中間配列を生成し、その中間配列の要素を数えます。


1
これは優れたFPソリューションであり、中間配列を作成する唯一の「問題」(ほとんどの場合は無関係)です。
tokland '25年

1
@tokland:それが心配なら、あなたはできるarray.reduce(function(total,x){return x==value? : total+1 : total}, 0)
ninjagecko 2012

1
@ninjagecko三項演算子にコロンが1つだけあるべきではありませんか? [...].reduce(function(total,x){return x==2 ? total+1 : total}, 0)
A.Krueger 2014年

2
@tokland多分フィルターは中間配列を作成しません。優れた最適化コンパイラは、配列の長さのみが使用されていることを簡単に認識できます。たぶん、現在のJSコンパイラはどれもこれを行うのに十分なほどスマートではありませんが、それは重要ではありません。ファウラーが言うように、「何かが痛いなら、それをもっとやる」。貧弱なコードを記述してコンパイラの欠陥を回避するのは近視眼です。コンパイラーに問題がある場合は、コンパイラーを修正してください。mlafeldt.github.io/blog/if-it-hurts-do-it-more-often
John Henckel

これを2017年の最適なソリューションと考えますconst count = (list) => list.filter((x) => x == 2).length。次にcount(list)、listが数値の配列であるを呼び出して使用します。const count = (list) => list.filter((x) => x.someProp === 'crazyValue').lengthオブジェクトの配列でcrazyValueのインスタンスをカウントすることもできます。これは、プロパティと完全に一致していることに注意してください。
agm1984

70

JSへのES6の更新:

===正しい比較を得るためには、常に三重の等号を使用する必要があることに注意してください。

// 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の全会一致のArrow関数(ラムダ関数):

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

単一の入力に対して、この簡潔な形式に簡略化できます。

x => 2 * x

どこをreturn暗示しています。


フィルター機能は、es6 for ofループを使用するよりもパフォーマンスが優れていますか?
Niklas

1
@Niklas、私はそれは同じだと思います(両方ともすべての要素をチェックする必要があるため、O(N)です)が、ブラウザに依存し、またキャッシュメモリに何が収まるかについては要素とコンピュータの数に依存すると思います。だから、私は答えは次のとおりだと思います: "It's complex" :)
Sverrisson

67

2017: 誰かがまだ質問に興味がある場合、私の解決策は次のとおりです:

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またはアンダースコアを使用している場合、_。countByメソッドは、配列内の各値をキーとする総計のオブジェクトを提供します。値を1つだけカウントする必要がある場合は、これをワンライナーに変換できます。

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

これは、数値の配列でも正常に機能します。あなたの例のワンライナーは次のようになります:

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

5
巨大すぎる。すべての一意の要素のカウンターを作成するため。ストレージと時間の無駄。
メタリム2017

5

私がこれをすることを考えることができる最も奇妙な方法は:

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

どこ:

  • aは配列です
  • nは配列内でカウントする数です

私の提案では、whileまたはforループを使用します;-)


3

ループを使用しないと、通常、いくつかの方法を介してプロセスを渡す意味ないループを使用します。

これは、私たちのループ嫌いコーダーが彼の嫌悪感を代償として満たすことができる方法です:

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

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


/*  returned value: (Number)
3
*/

配列メソッドとして使用できる場合は、indexOfを繰り返し呼び出して、毎回検索ポインターを移動することもできます。

これは新しい配列を作成せず、ループはforEachまたはフィルターよりも高速です。

100万人のメンバーを見ると、違いが出る可能性があります。

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
あなたはあなたの正規表現をちょうどに減らすことができますString(a).match(/2/g).length + 1-これに注意してください、またはあなたの実装は二桁ではうまくいかないでしょう。
ゲーリーグリーン

2
[2、22、2]はどうですか?
Oduvan 2017

2

フィルターなどの配列関数を使用して投稿されたソリューションのほとんどは、パラメーター化されていないため不完全です。

カウントする要素を実行時に設定できるソリューションを示します。

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

このアプローチの利点は、たとえばXより大きい要素の数をカウントするように関数を簡単に変更できることです。

インラインでreduce関数を宣言することもできます

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) ...は読みにくく、単にに勝る利点はありません... (acc, x) => acc += number == 2...+=代わりにの使用が好きacc + (number == 2)です。しかし、不当な構文HACKのように感じます。
masterxilo 2018年

2

本当に、なぜあなたは、必要があるだろうmapfilter、このために? reduceこれらの種類の操作のために「生まれた」:

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

それでおしまい!(item==val各反復の場合、に解決されるように、1がアキュムレータcountに追加されtrueます1)。

関数として:

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

または、先に進んでアレイを拡張します。

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

2

それを関数にラップする方が良いです:

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

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

2

ES2017 +で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;
});

オプションで出力をソートすることもできます。

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

例の配列のconsole.log(countsSorted):

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

1

あなたが探しているのは機能的なアプローチだと思います

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

elem === 'a'は条件です。独自のものに置き換えてください。


これは、3を印刷しませんが、0はそれを修正するには、あなたの2行目はあるべきcount = arr.filter(elem => elem === 'a').lengthcount = arr.filter(elem => {return elem === 'a'}).length
WPomier

1

私はjs配列のreduce関数の初心者です。

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

実際、もしあなたが本当に凝りたいのであれば、Arrayプロトタイプにcount関数を作成できます。その後、それを再利用できます。

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

それから

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

0

再帰による解決策

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
これは空の配列を処理しますか?
Andrew Grimm、2017年

@AndrewGrimmは正しいです。基本ケースはarr.length == 0です
Justin Meiners

素敵な解決策!私は練習のためだけに再帰を使用して何かをしようとしていましたが、あなたの例は私がやっていたことよりもエレガントでした。これは、を使用するよりも複雑な方法filterであるreduceか、単純なforLoopであり、パフォーマンスを確認する場合にはコストが高くなりますが、再帰を使用して実行する方法としては優れています。私の唯一の変更は次のとおりです。関数を作成し、その中にフィルターを追加して配列をコピーし、元の配列の変更を回避してから、内部関数として再帰を使用する方がよいと思います。
R.マルケス

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

私はそのようなことをします:

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

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

  console.log(dd);
}


0

コアレベルのファイルでArrayクラスの新しいメソッドを作成し、プロジェクト全体で使用します。

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

これをプロジェクトの任意の場所で使用します-

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

0

これはJavaScriptのワンライナーです。

  1. マップを使用します。(v === 2)配列で一致する値を見つけ、1と0の配列を返します。
  2. 削減を使用します。見つかった総数の配列のすべての値を追加します。
[1, 2, 3, 5, 2, 8, 9, 2]
  .map(function(v) {
    return v === 2 ? 1 : 0;
  })
  .reduce((a, b) => a + b, 0);

結果は3です。


0

実行方法に応じて:

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

JavaScript配列でlengthプロパティを使用できます:

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

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

最初にフィルタリングする場合は、長さを使用できます。つまり、array.filter(x => x === 2).length
Scott Blanch
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.