14

Say I have array like this:

[
   "foo",
   "bar",
   "foo"
   "bar",
   "bar",
   "bar",
   "zoom"
]

I want to group it so I can get a count like so:

{
  "foo": 2,
  "bar": 4,
  "zoom": 1
}

is there a utility that can do this?

6 Answers6

18

Just use the function Array.prototype.reduce.

let array = [   "foo",   "bar",   "foo",   "bar",   "bar",   "bar",   "zoom"],
    result = array.reduce((a, c) => (a[c] = (a[c] || 0) + 1, a), Object.create(null));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Ele
  • 33,468
  • 7
  • 37
  • 75
  • 1
    o/w people think there is a variable called `b` somewhere –  Oct 09 '18 at 00:36
  • 3
    you should also mention the `comma operator` and how it works...many people won't understand the syntax if you don't explain it or link to it. –  Oct 09 '18 at 00:37
  • 3
    If anyone was confused by the section `, Object.create(null)` like I was, the comma uses the [comma operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator) to return a null object. `Object.create(null)` is used rather than `{}` to create an [object with no prototype](https://stackoverflow.com/questions/15518328/creating-js-object-with-object-createnull) – bendataclear Mar 11 '20 at 10:19
  • Please incorpate @bendataclear 's suggestion, it works and it is more clear. array.reduce((a, c) => (a[c] = (a[c] || 0) + 1, a), {}) – VISHMAY Feb 19 '21 at 13:55
8

Yes you can reduce() your array to an object with the keys and the count, like this:

const input = [
  "foo",
   "bar",
   "foo",
   "bar",
   "bar",
   "bar",
   "zoom"
];
const result = input.reduce((total, value) => {
     total[value] = (total[value] || 0) + 1;
     return total;
}, {});
console.log(result);

Hope it helps!

Mocca
  • 399
  • 1
  • 9
4

You can do this in a concise way via reduce:

var arr = [ "foo", "bar", "foo", "bar", "bar", "bar", "zoom" ] 

var result = arr.reduce((r,c) => (r[c] = (r[c] || 0) + 1, r), {})

console.log(result)

It gets really cute if you ware to use lodash and _.countBy:

var arr = [ "foo", "bar", "foo", "bar", "bar", "bar", "zoom" ] 

var result = _.countBy(arr);

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Akrion
  • 18,117
  • 1
  • 34
  • 54
2

You could use the reduce() method which is avalible on the array object to achieve this grouping. So, something along the lines of this might achieve what you're after:

    var input = [
       "foo",
       "bar",
       "foo",
       "bar",
       "bar",
       "bar",
       "zoom"
    ]
    
    // Iterate the input list and construct a grouping via a "reducer"
    var output = input.reduce(function(grouping, item) {
    
      // If the current list item does not yet exist in grouping, set 
      // it's initial count to 1
      if( grouping[item] === undefined ) {    
        grouping[item] = 1;
      }
      // If current list item does exist in grouping, increment the 
      // count
      else {
        grouping[item] ++;
      }
    
      return grouping;
    
    }, {})
    
    console.log(output)
Dacre Denny
  • 29,664
  • 5
  • 45
  • 65
1

Yeah I guess with Array.prototype.reduce, it's just:

  const map = list.reduce((a, b) => {
            a[b] = a[b] || 0;
            return ++a[b], a;
          }, {});

wondering if there is less verbose way tho.

0

Explanation: First, we create object (a brand new empty object). Secondly using the spread operator we first copy everything from the existing array and make a new array out of it and use that to create a new Set (the Set object lets you store unique values) and use that as the key, and finally we filter out our array (with a little help from the length property) to see how many times a particular key occurs and set that to the object's value. And remember, since we used Object.assign() and put everything inside of it, so what gets returned is an object containing the result of everything that I tried to explain (to the best of my knowledge) above :) We also used the comma operator here which simply evaluates each of its operands (from left to right) and returns the value of the last operand.

const arr = ["foo", "bar", "foo", "bar", "bar", "bar", "zoom"];

const data = Object.assign({}, ...Array.from(new Set(arr), key => ({[key]: arr.filter(value => value === key).length})));

console.log(data);
Dženis H.
  • 7,284
  • 3
  • 25
  • 44