วิธีการนับองค์ประกอบบางอย่างในอาร์เรย์?


162

ฉันมีอาเรย์:

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

ฉันต้องการที่จะรู้ว่ามีกี่2s ในอาร์เรย์

เป็นวิธีที่ดีที่สุดในการทำใน JavaScript โดยไม่วนกับforวนคืออะไร

คำตอบ:


90

ง่ายมาก:

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

53
ไม่ว่าผมหมายถึงโดยไม่ต้องวนรอบด้วย "สำหรับ"
LEEM

9
@Leem: ทำไมลูปไม่ดี? บางครั้งมีการวนซ้ำ เห็นได้ชัดว่าคุณจะสร้างฟังก์ชั่นที่ซ่อนลูป เหล่านี้"ผมไม่ต้องการที่จะใช้เครื่องมือที่เหมาะสมสำหรับงาน" -requests ไม่เคยทำให้รู้สึกมากกับผม และเราสามารถโต้แย้งสิ่งที่งดงามที่สุด เช่นสำหรับฉันการเรียกใช้ฟังก์ชันต่อองค์ประกอบเพียงเพื่อเปรียบเทียบกับค่านั้นไม่ได้สวยงาม
เฟลิกซ์

2
สำหรับ laughs: alert (eval ('(' + 'my_array.join (' == 2) + (') +' == 2) '))) jsfiddle.net/gaby_de_wilde/gujbmych
user40521

29
OP อาจจะคิดว่าการวนซ้ำนั้นไม่ดีเพราะมันเป็นรหัส 5 บรรทัดและต้องการสถานะที่ไม่แน่นอน นักพัฒนาซอฟต์แวร์ที่มาอ่านว่าในภายหลังจะต้องใช้เวลาในการตรวจสอบสิ่งที่ทำและเสียสมาธิจากงานของพวกเขา สิ่งที่เป็นนามธรรมนั้นเหนือกว่ามากconst count = countItems(array, 2);และรายละเอียดการปฏิบัติสามารถโต้เถียงได้
joeytwiddle

2
นี่ไม่ใช่คำตอบที่ถูกต้องเพราะคำถามถามอย่างชัดเจนว่าไม่ได้ใช้ลูป ตรวจสอบโซลูชันของฉันที่ไม่ใช้ลูป stackoverflow.com/a/44743436/8211014
Luis Orantes

289

[ คำตอบนี้เก่าไปหน่อย: อ่านการแก้ไข ]

ทักทายเพื่อนของคุณ: mapและfilterและreduceและforEachและeveryอื่น ๆ

(ในบางครั้งฉันเขียนแบบ for-loops ใน javascript เท่านั้นเนื่องจากการกำหนดขอบเขตบล็อกหายไปดังนั้นคุณต้องใช้ฟังก์ชั่นเป็นเนื้อหาของลูปถ้าคุณต้องการจับหรือโคลนดัชนีการวนซ้ำหรือค่าของคุณ โดยทั่วไปจะมีประสิทธิภาพมากกว่า แต่บางครั้งคุณจำเป็นต้องปิดตัวลง)

วิธีที่อ่านง่ายที่สุด:

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

(เราน่าจะเขียน.filter(function(x){return x==2}).lengthแทน)

ต่อไปนี้เป็นพื้นที่ที่มีประสิทธิภาพมากขึ้น (O (1) มากกว่า O (N)) แต่ฉันไม่แน่ใจว่าคุณจะได้รับผลประโยชน์ / การลงโทษเท่าใดในแง่ของเวลา (ไม่มากกว่าปัจจัยคงที่ตั้งแต่คุณเยี่ยมชม แต่ละองค์ประกอบตรงหนึ่งครั้ง):

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

(หากคุณต้องการเพิ่มประสิทธิภาพโค้ดเฉพาะชิ้นนี้ a สำหรับลูปอาจเร็วกว่าในเบราว์เซอร์บางตัว ... คุณสามารถทดสอบสิ่งต่าง ๆ บน 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-loop แบบเก่า (ดูคำตอบอื่น ๆ ) ในคำจำกัดความด้านบน (อีกครั้งซึ่งน่าจะเร็วกว่ามาก)


2017 แก้ไข :

อ๊ะคำตอบนี้ได้รับความนิยมมากกว่าคำตอบที่ถูกต้อง ที่จริงแล้วเพียงใช้คำตอบที่ยอมรับได้ ในขณะที่คำตอบนี้อาจจะน่ารัก, js คอมไพเลอร์อาจไม่ (หรือไม่สามารถเนื่องจากสเป็ค) เพิ่มประสิทธิภาพกรณีดังกล่าว ดังนั้นคุณควรเขียนง่าย ๆ สำหรับลูป:

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เนื่องจากตัวเลขเช่น '4' == 4 ใน javascript ... ดังนั้นจึงเรียกมัน.countEqหรือ.countNonstrictเน้นว่าจะใช้==โอเปอเรเตอร์)

ลองพิจารณาใช้โครงสร้างข้อมูลหลายเซ็ตของคุณเอง (เช่น python ' collections.Counter') เพื่อหลีกเลี่ยงการนับในตอนแรก

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)

sidenote: แม้ว่าถ้าคุณยังต้องการวิธีการทำงานเขียนโปรแกรม (หรือใบปลิวหนึ่งซับโดยไม่ต้องเอาชนะ Array.prototype) [...].filter(x => x==2).lengthคุณสามารถเขียนมันมากขึ้นในปัจจุบันเป็นกะทัดรัด หากคุณใส่ใจกับประสิทธิภาพโปรดทราบว่าแม้ว่านี่จะเป็น asymptotically ประสิทธิภาพเดียวกันกับเวลา for-loop (O (N)) ก็อาจต้องใช้หน่วยความจำเสริม O (N) (แทน O (1) หน่วยความจำ) เพราะมันจะเกือบ แน่นอนสร้างอาร์เรย์กลางแล้วนับองค์ประกอบของอาร์เรย์กลางนั้น


1
นี่เป็นวิธีการแก้ปัญหา FP ที่ดีเพียง "ปัญหา" เท่านั้น (ไม่เกี่ยวข้องกับกรณีส่วนใหญ่) มันสร้างอาร์เรย์กลาง
tokland

1
@ โตคแลนด์: ถ้านั่นเป็นข้อกังวลคุณสามารถทำได้array.reduce(function(total,x){return x==value? : total+1 : total}, 0)
ninjagecko

1
@ninjagecko ไม่ควรมีเพียงหนึ่งโคลอนในผู้ประกอบการที่สาม? [...].reduce(function(total,x){return x==2 ? total+1 : total}, 0)
A.Krueger

2
@tokland บางทีตัวกรองจะไม่สร้างอาร์เรย์ระดับกลาง คอมไพเลอร์การปรับให้เหมาะสมที่ดีสามารถจดจำได้ง่ายว่ามีการใช้ความยาวของอาเรย์เท่านั้น อาจไม่มีคอมไพเลอร์ JS ปัจจุบันที่ฉลาดพอที่จะทำสิ่งนี้ แต่นั่นไม่สำคัญ ดังที่ฟาวเลอร์บอกว่า "ถ้ามีอะไรเจ็บก็จงทำมากกว่านี้" มันเป็นสายตาสั้นเพื่อหลีกเลี่ยงข้อบกพร่องของคอมไพเลอร์โดยการเขียนโค้ดที่ไม่ดี หากคอมไพเลอร์ sucks ให้แก้ไขคอมไพเลอร์ 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

71

อัพเดต ES6 เป็น JS:

โปรดทราบว่าคุณควรใช้สามเท่ากับเสมอ===เพื่อรับการเปรียบเทียบที่ถูกต้อง:

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

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

อาจลดความซับซ้อนของรูปแบบย่อนี้สำหรับอินพุตเดี่ยว:

x => 2 * x

ที่returnบอกเป็นนัย ๆ


ฟังก์ชั่นตัวกรองมีประสิทธิภาพมากกว่าการใช้ es6 สำหรับการวนซ้ำหรือไม่?
Niklas

1
@ Niklas ฉันคิดว่ามันเหมือนกัน (ทั้งคู่ต้องตรวจสอบองค์ประกอบทั้งหมด, O (N)) แต่ฉันคิดว่ามันขึ้นอยู่กับเบราว์เซอร์และยังขึ้นอยู่กับจำนวนขององค์ประกอบและคอมพิวเตอร์ที่เหมาะสมกับหน่วยความจำแคช ดังนั้นฉันเดาว่าคำตอบคือ: "มันซับซ้อน" :)
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จะจัดเตรียมออบเจ็กต์ของผลรวมรวมที่คีย์โดยแต่ละค่าในอาร์เรย์ คุณสามารถเปลี่ยนสิ่งนี้ให้เป็นหนึ่งซับได้ถ้าคุณเพียงต้องการนับหนึ่งค่า:

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

นอกจากนี้ยังทำงานได้ดีในอาร์เรย์ของตัวเลข ซับในสำหรับตัวอย่างของคุณคือ:

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

5
overkill มาก ในขณะที่มันสร้างเคาน์เตอร์สำหรับองค์ประกอบที่ไม่ซ้ำกันทั้งหมด เสียเวลาและการจัดเก็บ
metalim

5

วิธีที่แปลกประหลาดที่สุดที่ฉันสามารถทำได้คือ:

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

ที่ไหน:

  • aคืออาร์เรย์
  • nคือจำนวนที่จะนับในอาเรย์

ข้อเสนอแนะของฉันใช้เวลาสักครู่หรือเพื่อลูป ;-)


3

ไม่ได้ใช้ห่วงมักจะหมายถึงการมอบกระบวนการมากกว่าที่จะใช้วิธีการบางอย่างที่ไม่ใช้ห่วง

นี่เป็นวิธีที่ผู้ให้รหัสที่เกลียดชังของเราสามารถตอบสนองความเกลียดชังของเขาได้ในราคา:

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

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


/*  returned value: (Number)
3
*/

นอกจากนี้คุณยังสามารถเรียก indexOf ซ้ำ ๆ ได้หากมีวิธีการแบบอาร์เรย์และย้ายตัวชี้การค้นหาในแต่ละครั้ง

สิ่งนี้ไม่ได้สร้างอาร์เรย์ใหม่และการวนซ้ำนั้นเร็วกว่า forEach หรือตัวกรอง

มันอาจสร้างความแตกต่างได้หากคุณมีสมาชิกนับล้านคนที่จะดู

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
คุณสามารถลด regex ของคุณเป็นเพียงแค่String(a).match(/2/g).length + 1- แต่ระวังนี้หรือการใช้งานของคุณจะไม่เล่นดีกับตัวเลขสองหลัก
Gary Green

2
แล้ว [2, 22, 2] ล่ะ?
Oduvan

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

คุณอาจประกาศลดฟังก์ชั่นแบบอินไลน์

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)ว่า รู้สึกเหมือนแฮ็กไวยากรณ์ที่ไม่รับประกันว่า
masterxilo

2

จริง ๆ แล้วทำไมคุณต้องการmapหรือfilterเพื่อสิ่งนี้ 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 หากต้องการแก้ไขบรรทัดที่สองของคุณควรเป็นcount = arr.filter(elem => elem === 'a').lengthหรือcount = arr.filter(elem => {return elem === 'a'}).length
WPomier

1

ฉันเป็นแฟนตัวยงของฟังก์ชันลดอาร์เรย์ของ js

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

ในความเป็นจริงหากคุณต้องการได้รับแฟนซีคุณสามารถสร้างฟังก์ชั่นการนับบนต้นแบบ Array จากนั้นคุณสามารถใช้ซ้ำได้

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

@AndrewGrimm ถูกต้องแล้ว กรณีฐานคือ arr.length == 0
Justin Meiners

ทางออกที่ดี! ฉันพยายามทำบางสิ่งโดยใช้การเรียกซ้ำเพื่อการฝึกฝนและแบบอย่างของคุณก็สง่างามกว่าที่ฉันทำ มันเป็นวิธีที่ซับซ้อนกว่าการใช้filterงานreduceหรือใช้งานง่ายforLoopและมีราคาแพงกว่าเมื่อดูประสิทธิภาพ แต่ก็ยังเป็นวิธีที่ยอดเยี่ยมในการทำแบบเรียกซ้ำ การเปลี่ยนแปลงเพียงอย่างเดียวของฉัน: ฉันแค่คิดว่ามันจะเป็นการดีกว่าที่จะสร้างฟังก์ชั่นและเพิ่มตัวกรองภายในเพื่อคัดลอกอาร์เรย์และหลีกเลี่ยงการกลายพันธุ์ของอาร์เรย์เดิมจากนั้นใช้ฟังก์ชันเรียกซ้ำเป็นฟังก์ชันภายใน
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');
}

ฉันจะทำอะไรแบบนั้น

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 ในไฟล์ระดับ core และใช้ทั่วโครงการของคุณ

// 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)ในอาร์เรย์ส่งคืนอาร์เรย์ของค่าและศูนย์
  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:

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

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

หากคุณกรองมันเป็นครั้งแรกแล้วคุณสามารถใช้ระยะเวลา เช่น array.filter (x => x === 2) .length
สกอตต์ Blanch
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.