Consider the following simple array:
var foods = [\'hotdog\', \'hamburger\', \'soup\', \'sandwich\', \'hotdog\', \'watermelon\', \'hotdog\'];
Based off previous answers, here's my version to calculate mode for either an array of strings or numbers that accounts for draws in mode (when values have equal frequency) and also returns the frequency. You can then choose what to do with them.
Underscore and Lodash included.
// returns
// [['item', frequency]] where item is a string and frequency is an interger
// [['item', frequency], ['item', frequency], ['item', frequency] ... ] for draws
// examples:
// unique mode: creed appears the most times in array
// returns: [['creed', 4]]
// draw mode: 'jim' and 'creed' both occur 4 times in array
// returns: [['jim', 4], ['creed', 4]]
// underscore:
const usMode = arr => _.chain(arr).countBy().pairs().value().sort((a, b) => b[1] - a[1]).filter((e,i,a) => e[1] === a[0][1])
// lodash
const ldMode = arr => _.chain(arr).countBy().toPairs().value().sort((a, b) => b[1] - a[1]).filter((e,i,a) => e[1] === a[0][1])
// usage
usMode([1,2,3,3,3,4,5,6,7])
// [['3', 3]]
using underscore:
// underscore
const strsUniqueArr = ['jim','pam','creed','pam','jim','creed','creed','creed']
const strsDrawArr = ['pam','jim','creed','jim','jim','creed','creed','creed', 'pam', 'jim']
const numsUniqueArr = [1, 2, 2, 2, 2, 3 ,4, 5]
const numsDrawArr = [1, 1, 1, 1, 2, 2, 2, 2, 3 ,4, 5]
const usMode = arr => _.chain(arr).countBy().pairs().value().sort((a, b) => b[1] - a[1]).filter((e,i,a) => e[1] === a[0][1])
console.log('empty', usMode([]))
console.log('unique strs', usMode(strsUniqueArr))
console.log('draw sts', usMode(strsDrawArr))
console.log('unique nums', usMode(numsUniqueArr))
console.log('draw nums', usMode(numsDrawArr))
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>
using lodash:
// lodash
const strsUniqueArr = ['jim','pam','creed','pam','jim','creed','creed','creed']
const strsDrawArr = ['pam','jim','creed','jim','jim','creed','creed','creed', 'pam', 'jim']
const numsUniqueArr = [1, 2, 2, 2, 2, 3 ,4, 5]
const numsDrawArr = [1, 1, 1, 1, 2, 2, 2, 2, 3 ,4, 5]
const ldMode = arr => _.chain(arr).countBy().toPairs().value().sort((a, b) => b[1] - a[1]).filter((e,i,a) => e[1] === a[0][1])
console.log('empty', ldMode([]))
console.log('unique strs', ldMode(strsUniqueArr))
console.log('draw sts', ldMode(strsDrawArr))
console.log('unique nums', ldMode(numsUniqueArr))
console.log('draw nums', ldMode(numsDrawArr))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
var foods = ['hotdog', 'hamburger', 'soup', 'sandwich', 'hotdog', 'watermelon', 'hotdog'];
var result = _.chain(foods).countBy().pairs().max(_.last).head().value();
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js"></script>
countBy: Sorts a list into groups and returns a count for the number of objects in each group.
pairs: Convert an object into a list of
[key, value]
pairs.max: Returns the maximum value in list. If an iterator function is provided, it will be used on each value to generate the criterion by which the value is ranked.
last: Returns the last element of an array
head: Returns the first element of an array
chain: Returns a wrapped object. Calling methods on this object will continue to return wrapped objects until value is used.
value: Extracts the value of a wrapped object.
You can do this in one pass using _.reduce. The basic idea is to keep track of the word frequencies and the most common word at the same time:
var o = _(foods).reduce(function(o, s) {
o.freq[s] = (o.freq[s] || 0) + 1;
if(!o.freq[o.most] || o.freq[s] > o.freq[o.most])
o.most = s;
return o;
}, { freq: { }, most: '' });
That leaves 'hotdot'
in o.most
.
Demo: http://jsfiddle.net/ambiguous/G9W4m/
You can also do it with each (or even a simple for
loop) if you don't mind predeclaring the cache variable:
var o = { freq: { }, most: '' };
_(foods).each(function(s) {
o.freq[s] = (o.freq[s] || 0) + 1;
if(!o.freq[o.most] || o.freq[s] > o.freq[o.most])
o.most = s;
});
Demo: http://jsfiddle.net/ambiguous/WvXEV/
You could also break o
into two pieces and use a slightly modified version of the above, then you wouldn't have to say o.most
to get 'hotdog'
.