What you want to achieve is a frequency map. First, you can build a map of frequencies. By using the value in the array as a key, you can easily increment the count in the map.
The initial value for any newly-added key is: ((freq[val] || 0) + 1)
. This just means, grab the last value or 0, and increment by 1.
Once you have a map, you can then convert that to an array of name/count pairs.
let arr = ["red", "red", "green", "red", "green", "blue"];
let frequencyMap = arr.reduce((freq, val) => {
return { ...freq, [val]: ((freq[val] || 0) + 1) }
}, {});
let results = Object.keys(frequencyMap).map(key => {
return { name : key, count : frequencyMap[key] }
});
console.log(results);
.as-console-wrapper { top: 0; max-height: 100% !important; }
Code Golf
Here is a one-liner with a mapping function. :)
const freqArray = (a, fn) => ((f) => Object.keys(f).map(k => fn(k, f[k])))(a.reduce((m, v) => ({ ...m, [v]: ((m[v] || 0) + 1) }), {}));
let arr = [ "red", "red", "green", "red", "green", "blue" ];
let freq = freqArray(arr, (k, v) => ({ name : k, count : v }));
console.log(freq);
.as-console-wrapper { top: 0; max-height: 100% !important; }
Sorting the results
You could also sort the results by name, but if you do (since the keys are unique) there will be no need to sort by count, because the names will be unique.
const freqArray = (arr, opts) => ((freq) => Object.keys(freq).map(key => opts.mapFn(key, freq[key])))(arr.reduce((m, v) => ({ ...m, [v]: ((m[v] || 0) + 1) }), {})).sort(opts.cmpFn);
let arr = [ "red", "red", "green", "red", "green", "blue" ];
let freq = freqArray(arr, {
mapFn : (key, val) => ({ name : key, count : val }),
cmpFn : (a, b) => a.name.localeCompare(b.name) || (a.count - b.count)
});
console.log(freq);
.as-console-wrapper { top: 0; max-height: 100% !important; }
You can reverse the sort condition to sort by count (desc) then name:
const freqArray = (arr, opts) => ((freq) => Object.keys(freq).map(key => opts.mapFn(key, freq[key])))(arr.reduce((m, v) => ({ ...m, [v]: ((m[v] || 0) + 1) }), {})).sort(opts.cmpFn);
let arr = [ "red", "red", "green", "red", "green", "blue", "green", "blue", "blue" ];
let freq = freqArray(arr, {
mapFn : (key, val) => ({ name : key, count : val }),
cmpFn : (a, b) => (b.count - a.count) || a.name.localeCompare(b.name)
});
console.log(freq);
.as-console-wrapper { top: 0; max-height: 100% !important; }