Underscore.js: Find the most frequently occurring value in an array?

前端 未结 3 1941
醉话见心
醉话见心 2020-12-14 19:45

Consider the following simple array:

var foods = [\'hotdog\', \'hamburger\', \'soup\', \'sandwich\', \'hotdog\', \'watermelon\', \'hotdog\'];
相关标签:
3条回答
  • 2020-12-14 20:03

    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>

    0 讨论(0)
  • 2020-12-14 20:12

    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.

    0 讨论(0)
  • 2020-12-14 20:19

    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'.

    0 讨论(0)
提交回复
热议问题