Подсчет вхождений / частоты элементов массива
в Javascript я пытаюсь взять начальный массив числовых значений и подсчитать элементы внутри него. В идеале, результатом будет два новых массива, первый из которых определяет каждый уникальный элемент, а второй содержит количество раз, когда каждый элемент происходит. Тем не менее, я открыт для предложений по формату вывода.
например, если исходный массив был:
5, 5, 5, 2, 2, 2, 2, 2, 9, 4
тогда будут созданы два новых массива. Первый будет содержать имя каждого уникального элемент:
5, 2, 9, 4
второй будет содержать количество раз, когда этот элемент произошел в исходном массиве:
3, 5, 1, 1
поскольку число 5 встречается три раза в исходном массиве, число 2 встречается пять раз и 9 и 4 появляются один раз.
Я много искал решение, но ничего не работает, и все, что я пробовал сам, оказалось смехотворно сложным. Любая помощь будет оценена!
спасибо :)
26 ответов:
вот так:
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]; }
вы можете использовать объект для хранения результатов:
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]);Итак, теперь ваш объект counts может сказать вам, что такое счетчик для определенного числа:
console.log(counts[5]); // logs '3'если вы хотите получить массив членов, просто используйте
keys()функцииkeys(counts); // returns ["5", "2", "9", "4"]
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}
при использовании подчеркивания или 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()функции на результат, чтобы получить только уникальные номера, и их состояниях, соответственно. Но по моему опыту, с оригинальным объектом гораздо проще иметь дело.
не используйте два массива для результата, используйте объект:
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 }
как насчет опции 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конструктор в результате чего iterable
Я думаю, что это самый простой способ подсчета вхождений с одинаковым значением в массиве.
var a = [true, false, false, false]; a.filter(function(value){ return value === false; }).length
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))
Если вы предпочитаете один лайнер.
arr.reduce(function(countMap, word) {countMap[word] = ++countMap[word] || 1;return countMap}, {});Edit (6/12/2015): Объяснение изнутри наружу. countMap-это карта, которая отображает слово с его частотой, которую мы можем видеть анонимную функцию. То, что reduce делает, - это применить функцию с аргументами, поскольку все элементы массива и countMap передаются как возвращаемое значение последнего вызова функции. Последний параметр ({}) является значением по умолчанию countMap для первого вызова функции.
Если вы используете подчеркивание вы можете пойти функциональный маршрут
a = ['foo', 'foo', 'bar']; var results = _.reduce(a,function(counts,key){ counts[key]++; return counts }, _.object( _.map( _.uniq(a), function(key) { return [key, 0] })))Итак, ваш первый массив
_.keys(results)и второй массив
_.values(results)большая часть этого будет по умолчанию для собственных функций javascript, если они доступны
демо : http://jsfiddle.net/dAaUU/
на основе ответ на @adamse и @pmandell (который я повышаю), в ES6 вы можете сделать это одна строка:
- 2017 edit: Я использую
||чтобы сократить размер кода и сделать его более читабельным.var a=[7,1,7,2,2,7,3,3,3,7,,7,7,7]; alert(JSON.stringify( a.reduce((r,k)=>{r[k]=1+r[k]||1;return r},{}) ));
Он может быть использован для подсчет символов:
var s="ABRACADABRA"; alert(JSON.stringify( s.split('').reduce((a, c)=>{a[c]++?0:a[c]=1;return a},{}) ));
версия 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 }карта вместо простого объекта помогает нам различать различные типы элементов, иначе все подсчеты основаны на строках
вы можете расширить прототип массива, например:
Array.prototype.frequencies = function() { var l = this.length, result = {all:[]}; while (l--){ result[this[l]] = result[this[l]] ? ++result[this[l]] : 1; } // all pairs (label, frequencies) to an array of arrays(2) for (var l in result){ if (result.hasOwnProperty(l) && l !== 'all'){ result.all.push([ l,result[l] ]); } } return result; }; var freqs = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4].frequencies(); alert(freqs[2]); //=> 5 // or var freqs = '1,1,2,one,one,2,2,22,three,four,five,three,three,five' .split(',') .frequencies(); alert(freqs.three); //=> 3в качестве альтернативы вы можете использовать
Array.map:Array.prototype.frequencies = function () { var freqs = {sum: 0}; this.map( function (a){ if (!(a in this)) { this[a] = 1; } else { this[a] += 1; } this.sum += 1; return a; }, freqs ); return freqs; }
вот только что-то светлое и легкое для глаз...
function count(a,i){ var result = 0; for(var o in a) if(a[o] == i) result++; return result; }Edit: и так как вы хотите все вхождения...
function count(a){ var result = {}; for(var i in a){ if(result[a[i]] == undefined) result[a[i]] = 0; result[a[i]]++; } return result; }
var array = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]; function countDuplicates(obj, num){ obj[num] = (++obj[num] || 1); return obj; } var answer = array.reduce(countDuplicates, {}); // answer => {2:5, 4:1, 5:3, 9:1};если вы все еще хотите два массивы, то вы могли бы использовать ответ такой...
var uniqueNums = Object.keys(answer); // uniqueNums => ["2", "4", "5", "9"]; var countOfNums = Object.keys(answer).map(key => answer[key]); // countOfNums => [5, 1, 3, 1];или если вы хотите, чтобы uniqueNums были числами
var uniqueNums = Object.keys(answer).map(key => +key); // uniqueNums => [2, 4, 5, 9];
решение ES6 с уменьшением (фиксированным):
const arr = [2, 2, 2, 3, 2] const count = arr.reduce((pre, cur) => (cur === 2) ? ++pre : pre, 0) console.log(count) // 4
проверьте код ниже.
<html> <head> <script> // array with values var ar = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]; var Unique = []; // we'll store a list of unique values in here var Counts = []; // we'll store the number of occurances in here for(var i in ar) { var Index = ar[i]; Unique[Index] = ar[i]; if(typeof(Counts[Index])=='undefined') Counts[Index]=1; else Counts[Index]++; } // remove empty items Unique = Unique.filter(function(){ return true}); Counts = Counts.filter(function(){ return true}); alert(ar.join(',')); alert(Unique.join(',')); alert(Counts.join(',')); var a=[]; for(var i=0; i<Unique.length; i++) { a.push(Unique[i] + ':' + Counts[i] + 'x'); } alert(a.join(', ')); </script> </head> <body> </body> </html>
попробуйте это:
Array.prototype.getItemCount = function(item) { var counts = {}; for(var i = 0; i< this.length; i++) { var num = this[i]; counts[num] = counts[num] ? counts[num]+1 : 1; } return counts[item] || 0; }
заданный массив x я.е
x = ['boy','man','oldman','scout','pilot'];количество вхождений элемента'man'иx.length - x.toString().split(',man,').toString().split(',').length ;
я решал аналогичную проблему на codewars и разработал следующее решение, которое сработало для меня.
Это дает наибольшее количество целого числа в массиве, а также само целое число. Я думаю, что это может быть применено и к строковому массиву.
чтобы правильно отсортировать строки, удалите
function(a, b){return a-b}внутриsort()частьfunction mostFrequentItemCount(collection) { collection.sort(function(a, b){return a-b}); var i=0; var ans=[]; var int_ans=[]; while(i<collection.length) { if(collection[i]===collection[i+1]) { int_ans.push(collection[i]); } else { int_ans.push(collection[i]); ans.push(int_ans); int_ans=[]; } i++; } var high_count=0; var high_ans; i=0; while(i<ans.length) { if(ans[i].length>high_count) { high_count=ans[i].length; high_ans=ans[i][0]; } i++; } return high_ans; }
есть гораздо лучше и простой способ, что мы можем сделать это с помощью
ramda.js. пример кода здесь
const ary = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]; R.countBy(r=> r)(ary)документация countBy находится по адресу документация
в отношении моего комментария, спрашивающего @Emissary о корректировке его решения. я добавляю, как я справился с этим:
let distinctArr = yourArray.filter((curElement, index, array) => array.findIndex(t => t.prop1=== curElement.prop1 && t.prop2 === curElement.prop2 && t.prop3=== curElement.prop3) === index); let distinctWithCount = [...new Set(distinctArr)].map(function(element){element.prop4 = yourArray.filter(t => t.prop1=== element.prop1 && t.prop2 === element.prop2 && t.prop2=== element.prop2).length;что я делаю здесь, 1-е удаление дубликатов и сохранение массива (distinctArr) затем я рассчитываю на исходный массив (yourArray) количество времени, в течение которого объект был дублирован, и добавление 4-го свойства со значением вхождений
надеюсь, что это поможет для кого-то, кто нуждается в этом конкретном решении ОФК это сделано с ES6
вот способ подсчета вхождений в массив объектов. Он также помещает содержимое первого массива в новый массив для сортировки значений, чтобы порядок в исходном массиве не нарушался. Затем рекурсивная функция используется для прохождения каждого элемента и подсчета свойства quantity каждого объекта внутри массива.
var big_array = [ { name: "Pineapples", quantity: 3 }, { name: "Pineapples", quantity: 1 }, { name: "Bananas", quantity: 1 }, { name: "Limes", quantity: 1 }, { name: "Bananas", quantity: 1 }, { name: "Pineapples", quantity: 2 }, { name: "Pineapples", quantity: 1 }, { name: "Bananas", quantity: 1 }, { name: "Bananas", quantity: 1 }, { name: "Bananas", quantity: 5 }, { name: "Coconuts", quantity: 1 }, { name: "Lemons", quantity: 2 }, { name: "Oranges", quantity: 1 }, { name: "Lemons", quantity: 1 }, { name: "Limes", quantity: 1 }, { name: "Grapefruit", quantity: 1 }, { name: "Coconuts", quantity: 5 }, { name: "Oranges", quantity: 6 } ]; function countThem() { var names_array = []; for (var i = 0; i < big_array.length; i++) { names_array.push( Object.assign({}, big_array[i]) ); } function outerHolder(item_array) { if (item_array.length > 0) { var occurrences = []; var counter = 0; var bgarlen = item_array.length; item_array.sort(function(a, b) { return (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0); }); function recursiveCounter() { occurrences.push(item_array[0]); item_array.splice(0, 1); var last_occurrence_element = occurrences.length - 1; var last_occurrence_entry = occurrences[last_occurrence_element].name; var occur_counter = 0; var quantity_counter = 0; for (var i = 0; i < occurrences.length; i++) { if (occurrences[i].name === last_occurrence_entry) { occur_counter = occur_counter + 1; if (occur_counter === 1) { quantity_counter = occurrences[i].quantity; } else { quantity_counter = quantity_counter + occurrences[i].quantity; } } } if (occur_counter > 1) { var current_match = occurrences.length - 2; occurrences[current_match].quantity = quantity_counter; occurrences.splice(last_occurrence_element, 1); } counter = counter + 1; if (counter < bgarlen) { recursiveCounter(); } } recursiveCounter(); return occurrences; } } alert(JSON.stringify(outerHolder(names_array))); }
function countOcurrences(arr){ return arr.reduce((aggregator, value, index, array) => { if(!aggregator[value]){ return aggregator = {...aggregator, [value]: 1}; }else{ return aggregator = {...aggregator, [value]:++aggregator[value]}; } }, {}) }
вот классический метод старой школы для подсчета массивов.
var arr = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]; var counted = [], count = []; var i = 0, j = 0, k = 0; while (k < arr.length) { if (counted.indexOf(arr[k]) < 0) { counted[i] = arr[k]; count[i] = 0; for (j = 0; j < arr.length; j++) { if (counted[i] == arr[j]) { count[i]++; } } i++; } else { k++; } }вы можете сначала отсортировать его, если хотите получить результат в алфавитном порядке, но если вы хотите сохранить порядок, в котором были введены данные, попробуйте это сделать. Вложенные циклы могут быть немного медленнее, чем некоторые другие методы на этой странице.
Comments