Làm thế nào để đếm các yếu tố nhất định trong mảng?


161

Tôi có một mảng:

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

Tôi muốn biết có bao nhiêu 2s trong mảng.

Cách thanh lịch nhất để làm điều đó trong JavaScript mà không lặp với forvòng lặp là gì?

Câu trả lời:


90

Rất đơn giản:

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

53
Không, ý tôi là không lặp lại với "cho"
Leem

9
@Leem: Tại sao vòng lặp xấu? Luôn luôn có vòng lặp ở một số điểm. Rõ ràng bạn sẽ tạo ra một hàm ẩn vòng lặp. Những "Tôi không muốn sử dụng công cụ phù hợp cho công việc" -các câu hỏi không bao giờ có ý nghĩa đối với tôi. Và chúng ta có thể tranh luận những gì là thanh lịch nhất. Ví dụ, đối với tôi, thực hiện một lệnh gọi hàm cho mỗi phần tử để chỉ so sánh nó với một giá trị là không thanh lịch.
Felix Kling

2
cho cười: alert (eval ( '(' + my_array.join ( '== 2) + (') + '== 2)')) jsfiddle.net/gaby_de_wilde/gujbmych
user40521

29
OP có thể nghĩ rằng vòng lặp là xấu vì nó là 5 dòng mã và yêu cầu trạng thái có thể thay đổi. Một nhà phát triển đến để đọc rằng sau này sẽ phải dành thời gian để kiểm tra xem nó làm gì và mất tập trung từ nhiệm vụ của họ. Một sự trừu tượng là vượt trội hơn nhiều: const count = countItems(array, 2);và các chi tiết thực hiện có thể được tranh luận bên trong.
joeytwiddle

2
Đây không phải là câu trả lời đúng bởi vì câu hỏi rõ ràng yêu cầu không sử dụng các vòng lặp. Kiểm tra giải pháp của tôi mà không sử dụng các vòng lặp. stackoverflow.com/a/44743436/8211014
Luis Orantes

287

[ câu trả lời này là một chút ngày: đọc các chỉnh sửa ]

Say chào bạn bè: mapfilterreduceforEacheveryvv

(Tôi chỉ thỉnh thoảng viết các vòng lặp for trong javascript, vì thiếu phạm vi cấp độ khối, do đó bạn phải sử dụng một hàm làm phần thân của vòng lặp nếu bạn cần phải nắm bắt hoặc sao chép chỉ số hoặc giá trị lặp của mình. thường hiệu quả hơn, nhưng đôi khi bạn cần đóng cửa.)

Cách dễ đọc nhất:

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

(Chúng ta có thể đã viết .filter(function(x){return x==2}).lengththay thế)

Dưới đây là không gian hiệu quả hơn (O (1) thay vì O (N)), nhưng tôi không chắc bạn có thể trả bao nhiêu lợi ích / hình phạt về thời gian (không nhiều hơn một yếu tố không đổi kể từ khi bạn truy cập mỗi yếu tố chính xác một lần):

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

(Nếu bạn cần tối ưu hóa đoạn mã cụ thể này, vòng lặp for có thể nhanh hơn trên một số trình duyệt ... bạn có thể kiểm tra mọi thứ trên jsperf.com.)


Sau đó, bạn có thể thanh lịch và biến nó thành một chức năng nguyên mẫu:

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

Như thế này:

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

Bạn cũng có thể sử dụng kỹ thuật for-loop cũ thông thường (xem các câu trả lời khác) bên trong định nghĩa thuộc tính ở trên (một lần nữa, điều đó có thể sẽ nhanh hơn nhiều).


Chỉnh sửa năm 2017 :

Rất tiếc, câu trả lời này đã trở nên phổ biến hơn câu trả lời đúng. Trên thực tế, chỉ cần sử dụng câu trả lời được chấp nhận. Trong khi câu trả lời này có thể dễ thương, trình biên dịch js có thể không (hoặc không thể do thông số kỹ thuật) tối ưu hóa các trường hợp như vậy. Vì vậy, bạn thực sự nên viết một vòng lặp đơn giản:

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

Bạn có thể định nghĩa một phiên bản .countStrictEq(...)sử dụng ===khái niệm bình đẳng. Khái niệm bình đẳng có thể quan trọng đối với những gì bạn đang làm! (ví dụ: [1,10,3,'10'].count(10)==2vì các số như '4' == 4 trong javascript ... do đó gọi nó .countEqhoặc .countNonstrictnhấn mạnh nó sử dụng== toán tử.)

Ngoài ra, hãy cân nhắc sử dụng cấu trúc dữ liệu nhiều trang của riêng bạn (ví dụ như python ' collections.Counter') để tránh phải đếm ở vị trí đầu tiê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 {}
    }
}

Bản giới thiệu:

> 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: Mặc dù, nếu bạn vẫn muốn cách lập trình chức năng (hoặc một lớp lót bỏ đi mà không ghi đè Array.prototype), ngày nay bạn có thể viết nó chặt chẽ hơn [...].filter(x => x==2).length. Nếu bạn quan tâm đến hiệu suất, lưu ý rằng mặc dù đây không phải là hiệu suất tương tự như thời gian for-loop (O (N)), nó có thể yêu cầu bộ nhớ phụ O (N) (thay vì bộ nhớ O (1)) vì nó gần như sẽ chắc chắn tạo ra một mảng trung gian và sau đó đếm các phần tử của mảng trung gian đó.


1
Đây là một giải pháp FP tốt, "vấn đề" duy nhất (không liên quan đến hầu hết các trường hợp) nó tạo ra một mảng trung gian.
tokland

1
@tokland: Nếu đó là một mối quan tâm, bạn có thể làmarray.reduce(function(total,x){return x==value? : total+1 : total}, 0)
ninjagecko

1
@ninjagecko Không nên chỉ có một dấu hai chấm trong toán tử ternary? [...].reduce(function(total,x){return x==2 ? total+1 : total}, 0)
A.Krueger

2
@tokland Có thể bộ lọc sẽ không tạo ra một mảng trung gian. Một trình biên dịch tối ưu hóa tốt có thể dễ dàng nhận ra rằng chỉ có độ dài của mảng được sử dụng. Có thể không có trình biên dịch JS hiện tại nào đủ thông minh để làm điều này, nhưng điều đó không quan trọng. Như Fowler nói, "Nếu có gì đó đau, thì hãy làm nhiều hơn nữa". Thật thiển cận để tránh sự thiếu hụt trình biên dịch bằng cách viết mã kém. Nếu trình biên dịch hút, sửa trình biên dịch. mlafeldt.github.io/blog/if-it-hurts-do-it-more-often
John Henckel

Tôi sẽ coi đây là một giải pháp tối ưu 2017 : const count = (list) => list.filter((x) => x == 2).length. Sau đó sử dụng nó bằng cách gọi count(list)nơi danh sách là một mảng các số. Bạn cũng có thể làm const count = (list) => list.filter((x) => x.someProp === 'crazyValue').lengthđể đếm các thể hiện của crazyValue trong mảng các đối tượng. Lưu ý, nó là một kết hợp chính xác cho tài sản.
agm1984

70

Cập nhật ES6 lên JS:

Lưu ý rằng bạn phải luôn luôn sử dụng ba bằng: ===để có được so sánh chính xác:

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

Hàm mũi tên nhất trí sau (hàm lambda) trong JS:

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

có thể được đơn giản hóa thành dạng ngắn gọn này cho một đầu vào:

x => 2 * x

nơi returnđược ngụ ý.


Là chức năng lọc có hiệu suất cao hơn so với sử dụng es6 cho vòng lặp?
Niklas

1
@Niklas, tôi nghĩ nó giống nhau (vì cả hai đều phải kiểm tra tất cả các yếu tố, O (N)), nhưng, tôi đoán nó phụ thuộc vào trình duyệt và cả số lượng phần tử và máy tính phù hợp với bộ nhớ đệm. Vì vậy, tôi đoán câu trả lời là: "Nó phức tạp" :)
Sverrisson

67

2017: Nếu ai đó vẫn quan tâm đến câu hỏi, giải pháp của tôi là như sau:

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

Nếu bạn đang sử dụng lodash hoặc gạch dưới, phương thức _.countBy sẽ cung cấp một đối tượng tổng số tổng được khóa theo từng giá trị trong mảng. Bạn có thể biến điều này thành một lớp lót nếu bạn chỉ cần đếm một giá trị:

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

Điều này cũng hoạt động tốt trên mảng số. Một ví dụ cho ví dụ của bạn sẽ là:

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

5
Quá mức cần thiết. Vì nó tạo ra các quầy cho tất cả các yếu tố độc đáo. Lưu trữ lãng phí và thời gian.
metalim

5

Cách kỳ lạ nhất tôi có thể nghĩ làm việc này là:

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

Ở đâu:

  • a là mảng
  • n là số cần đếm trong mảng

Đề nghị của tôi, sử dụng một thời gian hoặc cho vòng lặp ;-)


3

Không sử dụng một vòng lặp thường có nghĩa là trao quá trình này qua một số phương pháp mà không sử dụng một vòng lặp.

Đây là một cách mà lập trình viên ghét vòng lặp của chúng tôi có thể thỏa mãn sự ghê tởm của anh ta, với mức giá:

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

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


/*  returned value: (Number)
3
*/

Bạn cũng có thể liên tục gọi indexOf, nếu nó có sẵn dưới dạng phương thức mảng và di chuyển con trỏ tìm kiếm mỗi lần.

Điều này không tạo ra một mảng mới và vòng lặp nhanh hơn forEach hoặc bộ lọc.

Nó có thể tạo sự khác biệt nếu bạn có một triệu thành viên để xem xét.

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
Bạn có thể giảm regex của mình xuống chỉ String(a).match(/2/g).length + 1- mặc dù vậy, hãy cẩn thận điều này hoặc việc triển khai của bạn sẽ không hoạt động tốt với hai chữ số.
Gary Green

2
[2, 22, 2] thì sao?
Oduvan

2

Hầu hết các giải pháp được đăng sử dụng các hàm mảng như bộ lọc đều không đầy đủ vì chúng không được tham số hóa.

Ở đây có một giải pháp mà phần tử cần đếm có thể được đặt khi chạy.

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

Ưu điểm của phương pháp này là có thể dễ dàng thay đổi hàm để tính ví dụ số lượng phần tử lớn hơn X.

Bạn cũng có thể khai báo hàm giảm nội tuyến

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) ...khó đọc hơn và không có lợi thế hơn chỉ ... (acc, x) => acc += number == 2.... Tôi thích sử dụng của bạn +=thay vì acc + (number == 2)mặc dù. Cảm thấy như một cú pháp không chính đáng HACK mặc dù.
masterxilo

2

Thực sự, tại sao bạn cần maphoặc filtercho điều này? reduceđược "sinh ra" cho các loại hoạt động này:

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

đó là nó! (nếu item==valtrong mỗi lần lặp, thì 1 sẽ được thêm vào bộ tích lũy count, như truesẽ giải quyết 1).

Là một chức năng:

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

Hoặc, tiếp tục và mở rộng mảng của bạn:

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

2

Nó là tốt hơn để bọc nó vào chức năng:

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

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

2

Dưới đây là cách ES2017 + để tính tổng số cho tất cả các mục mảng trong 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;
});

Bạn cũng có thể tùy ý sắp xếp đầu ra:

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

console.log (CountsSort) cho mảng ví dụ của bạn:

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

1

Tôi tin rằng những gì bạn đang tìm kiếm là phương pháp chức năng

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

elem === 'a' là điều kiện, thay thế nó bằng của riêng bạn.


Nó sẽ không in 3, nhưng 0. Để khắc phục điều đó, dòng thứ hai của bạn nên được count = arr.filter(elem => elem === 'a').lengthhoặccount = arr.filter(elem => {return elem === 'a'}).length
WPomier

1

Tôi là một fan hâm mộ mới của chức năng giảm của mảng js.

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

Trong thực tế nếu bạn thực sự muốn có được sự ưa thích, bạn có thể tạo một hàm đếm trên nguyên mẫu Array. Sau đó, bạn có thể tái sử dụng nó.

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

Sau đó làm

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

0

Giải pháp bằng cách đệ quy

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
Điều này có xử lý một mảng trống?
Andrew Grimm

@AndrewGrimm có quyền. Trường hợp cơ sở là Array.length == 0
Justin Meiners

Giải pháp tốt đẹp! Tôi đã cố gắng làm một cái gì đó bằng cách sử dụng đệ quy chỉ để thực hành và ví dụ của bạn thanh lịch hơn những gì tôi đang làm. Đó chắc chắn là một cách phức tạp hơn so với việc sử dụng filter, reducehoặc đơn giản forLoopvà cũng tốn kém hơn khi xem hiệu suất, nhưng vẫn là một cách tuyệt vời để làm điều đó với đệ quy. Thay đổi duy nhất của tôi là: Tôi chỉ nghĩ sẽ tốt hơn nếu tạo một hàm và thêm một bộ lọc bên trong nó để sao chép mảng và tránh sự đột biến của mảng ban đầu, sau đó sử dụng đệ quy làm hàm bên trong.
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');
}

Tôi sẽ làm một cái gì đó như thế:

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

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

  console.log(dd);
}


0

Tạo một phương thức mới cho lớp Array trong tệp mức lõi và sử dụng nó trên toàn bộ dự án của bạn.

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

Sử dụng điều này bất cứ nơi nào trong dự án của bạn -

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

0

Đây là một lót trong javascript.

  1. Sử dụng bản đồ. Tìm các giá trị khớp(v === 2) trong mảng, trả về một mảng gồm các số và số không.
  2. Sử dụng Giảm. Thêm tất cả các giá trị của mảng cho tổng số tìm thấy.
[1, 2, 3, 5, 2, 8, 9, 2]
  .map(function(v) {
    return v === 2 ? 1 : 0;
  })
  .reduce((a, b) => a + b, 0);

Kết quả là 3.


0

Tùy thuộc vào cách bạn muốn chạy nó:

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

Bạn có thể sử dụng thuộc tính độ dài trong mảng JavaScript:

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

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

Nếu bạn lọc nó trước thì bạn có thể sử dụng chiều dài. tức là mảng.filter (x => x === 2) .length
Scott Blanch
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.