Подсчет вхождений / частоты элементов массива - PullRequest
184 голосов
/ 14 апреля 2011

В Javascript я пытаюсь взять начальный массив числовых значений и сосчитать элементы внутри него.В идеале, результатом должны быть два новых массива, первый из которых определяет каждый уникальный элемент, а второй содержит количество раз, которое встречается каждый элемент.Однако я открыт для предложений по формату вывода.

Например, если начальный массив был:

5, 5, 5, 2, 2, 2, 2, 2, 9, 4

Тогда будут созданы два новых массива.Первый будет содержать имя каждого уникального элемента:

5, 2, 9, 4

Второй будет содержать число раз, которое элемент встречался в исходном массиве:

3, 5, 1, 1

Поскольку число 5 происходиттри раза в исходном массиве число 2 встречается пять раз, а 9 и 4 появляются один раз.

Я много искал решение, но, похоже, ничего не работает, и все, что я пробовал самв итоге оказался нелепо сложным.Любая помощь будет оценена!

Спасибо:)

Ответы [ 35 ]

185 голосов
/ 14 апреля 2011

Вы можете использовать объект для хранения результатов:

var arr = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
var counts = {};

for (var i = 0; i < arr.length; i++) {
  var num = arr[i];
  counts[num] = counts[num] ? counts[num] + 1 : 1;
}

console.log(counts[5], counts[2], counts[9], counts[4]);

Итак, теперь ваш объект подсчета может сказать вам, что подсчитано для определенного числа:

console.log(counts[5]); // logs '3'

Если вы хотите получить массив членов, просто используйте keys() functions

keys(counts); // returns ["5", "2", "9", "4"]
81 голосов
/ 14 апреля 2011

Вот, пожалуйста,

function foo(arr) {
    var a = [], b = [], prev;

    arr.sort();
    for ( var i = 0; i < arr.length; i++ ) {
        if ( arr[i] !== prev ) {
            a.push(arr[i]);
            b.push(1);
        } else {
            b[b.length-1]++;
        }
        prev = arr[i];
    }

    return [a, b];
}

Демонстрационная версия: http://jsfiddle.net/simevidas/bnACW/

Примечание

Это меняет порядокисходный массив ввода с использованием Array.sort

78 голосов
/ 15 апреля 2011
var a = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4].reduce(function (acc, curr) {
  if (typeof acc[curr] == 'undefined') {
    acc[curr] = 1;
  } else {
    acc[curr] += 1;
  }

  return acc;
}, {});

// a == {2: 5, 4: 1, 5: 3, 9: 1}
64 голосов
/ 31 января 2015

Если вы используете подчеркивание или lodash, это самое простое:

_.countBy(array);

Так, что:

_.countBy([5, 5, 5, 2, 2, 2, 2, 2, 9, 4])
=> Object {2: 5, 4: 1, 5: 3, 9: 1}

Как указано другими, вы можете затем выполнить _.keys() и _.values() работают с результатом, чтобы получить только уникальные числа и их вхождения соответственно.Но по моему опыту, с оригинальным объектом гораздо проще иметь дело.

51 голосов
/ 14 апреля 2011

Не используйте два массива для результата, используйте объект:

a      = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
result = { };
for(var i = 0; i < a.length; ++i) {
    if(!result[a[i]])
        result[a[i]] = 0;
    ++result[a[i]];
}

Тогда result будет выглядеть так:

{
    2: 5,
    4: 1,
    5: 3,
    9: 1
}
37 голосов
/ 30 января 2016

Как насчет опции ECMAScript2015.

const a = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];

const aCount = new Map([...new Set(a)].map(
    x => [x, a.filter(y => y === x).length]
));
aCount.get(5)  // 3
aCount.get(2)  // 5
aCount.get(9)  // 1
aCount.get(4)  // 1

В этом примере массив входных данных передается конструктору Set, создавая коллекцию уникальных ценности.Синтаксис затем расширяет эти значения в новый массив, так что мы можем вызвать map и преобразовать его в двумерный массив из [value, count] пар, то есть следующую структуру:

Array [
   [5, 3],
   [2, 5],
   [9, 1],
   [4, 1]
]

Новый массив затем передается конструктору Map, в результате чего получается повторяемый объект:

Map {
    5 => 3,
    2 => 5,
    9 => 1,
    4 => 1
}

Отличная вещь о MapОбъект в том, что он сохраняет типы данных - то есть aCount.get(5) вернет 3, но aCount.get("5") вернет undefined.Он также позволяет любому значению / типу выступать в качестве ключа, что означает, что это решение также будет работать с массивом объектов.

function frequencies(/* {Array} */ a){
    return new Map([...new Set(a)].map(
        x => [x, a.filter(y => y === x).length]
    ));
}

let foo = { value: 'foo' },
    bar = { value: 'bar' },
    baz = { value: 'baz' };

let aNumbers = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4],
    aObjects = [foo, bar, foo, foo, baz, bar];

frequencies(aNumbers).forEach((val, key) => console.log(key + ': ' + val));
frequencies(aObjects).forEach((val, key) => console.log(key.value + ': ' + val));
29 голосов
/ 16 октября 2014

Мне кажется, это самый простой способ подсчета вхождений с одинаковым значением в массиве.

var a = [true, false, false, false];
a.filter(function(value){
    return value === false;
}).length
26 голосов
/ 04 октября 2016

const data = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]

function count(arr) {
  return arr.reduce((prev, curr) => (prev[curr] = ++prev[curr] || 1, prev), {})
}

console.log(count(data))
20 голосов
/ 03 марта 2015

Если вы предпочитаете один вкладыш.

arr.reduce(function(countMap, word) {countMap[word] = ++countMap[word] || 1;return countMap}, {});

Редактировать (6/12/2015) : объяснение изнутри.countMap - это карта, которая отображает слово с его частотой, которую мы видим в анонимной функции.Что уменьшает, так это применяет функцию с аргументами в качестве всех элементов массива, а countMap передается как возвращаемое значение последнего вызова функции.Последний параметр ({}) является значением по умолчанию countMap для первого вызова функции.

15 голосов
/ 07 сентября 2017

Версия ES6 должна быть намного проще (еще одно однострочное решение)

let arr = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
let acc = arr.reduce((acc, val) => acc.set(val, 1 + (acc.get(val) || 0)), new Map());

console.log(acc);
// output: Map { 5 => 3, 2 => 5, 9 => 1, 4 => 1 }

Карта вместо простого Объекта, помогающая нам различать различные типы элементов, иначе все счета основаны на строках

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...