HiveBrain v1.2.0
Get Started
← Back to all entries
snippetjavascriptTip

How can I group and count values in a JavaScript array?

Submitted by: @import:30-seconds-of-code··
0
Viewed 0 times
javascriptgroupandhowcountcanarrayvalues

Problem

Finding the count of each value in an array can come in handy in a lot of situations. It's also fairly straightforward to implement, both for primitive and complex values, using JavaScript's Array methods.
You can use Array.prototype.reduce() to create an object with the unique values of an array as keys and their frequencies as the values. Use the nullish coalescing operator (??) to initialize the value of each key to 0 if it doesn't exist and increment it by 1 every time the same value is encountered.
You can also group the elements of an array based on a given function and return the count of elements in each group. This can be useful when you want to group elements based on a specific property or a function.
To do so, you can use Array.prototype.map() to map the values of an array to a function or property name, and then use Array.prototype.reduce() to create an object, where the keys are produced from the mapped results.
Both of the previous examples use objects to store the frequencies of the values. However, you can also use a Map to store the frequencies. This can be useful if you need to keep the insertion order of the keys or if you need to iterate over the keys in the order they were inserted. It's also more efficient when you need to delete keys or check for the existence of a key.

Solution

const frequencies = arr =>
  arr.reduce((a, v) => {
    a[v] = (a[v] ?? 0) + 1;
    return a;
  }, {});

frequencies(['a', 'b', 'a', 'c', 'a', 'a', 'b']);
// { a: 4, b: 2, c: 1 }
frequencies([...'ball']);
// { b: 1, a: 1, l: 2 }


You can also group the elements of an array based on a given function and return the count of elements in each group. This can be useful when you want to group elements based on a specific property or a function.
To do so, you can use Array.prototype.map() to map the values of an array to a function or property name, and then use Array.prototype.reduce() to create an object, where the keys are produced from the mapped results.
Both of the previous examples use objects to store the frequencies of the values. However, you can also use a Map to store the frequencies. This can be useful if you need to keep the insertion order of the keys or if you need to iterate over the keys in the order they were inserted. It's also more efficient when you need to delete keys or check for the existence of a key.
When dealing with data that changes often, you may need to recalculate frequencies as needed. This can become tedious and inefficient, especially if you only need to keep track of the frequencies and have no need for the original array.
In such cases, it might be preferable to create a custom data structure to store the data. This data structure will be able to keep track of the frequencies of the values it contains and update them as needed.
Moreover, you may want to be able to check the frequency of any value, even if it doesn't exist in the data structure. This also comes in handy if you want to easily increment or decrement the frequency of a value. Let's take a look at how you can implement such a data structure:

Code Snippets

const frequencies = arr =>
  arr.reduce((a, v) => {
    a[v] = (a[v] ?? 0) + 1;
    return a;
  }, {});

frequencies(['a', 'b', 'a', 'c', 'a', 'a', 'b']);
// { a: 4, b: 2, c: 1 }
frequencies([...'ball']);
// { b: 1, a: 1, l: 2 }
const countBy = (arr, fn) =>
  arr
    .map(typeof fn === 'function' ? fn : val => val[fn])
    .reduce((acc, val) => {
      acc[val] = (acc[val] || 0) + 1;
      return acc;
    }, {});

countBy([6.1, 4.2, 6.3], Math.floor);
// {4: 1, 6: 2}
countBy(['one', 'two', 'three'], 'length');
// {3: 2, 5: 1}
countBy([{ count: 5 }, { count: 10 }, { count: 5 }], x => x.count);
// {5: 2, 10: 1}
const frequenciesMap = arr =>
  arr.reduce((a, v) => a.set(v, (a.get(v) ?? 0) + 1), new Map());

frequenciesMap(['a', 'b', 'a', 'c', 'a', 'a', 'b']);
// Map(3) { 'a' => 4, 'b' => 2, 'c' => 1 }

const countByMap = (arr, fn) =>
  arr
    .map(typeof fn === 'function' ? fn : val => val[fn])
    .reduce((acc, val) => {
      acc.set(val, (acc.get(val) || 0) + 1);
      return acc;
    }, new Map());

countByMap([6.1, 4.2, 6.3], Math.floor);
// Map(2) { 6 => 2, 4 => 1 }
countByMap(['one', 'two', 'three'], 'length');
// Map(2) { 3 => 2, 5 => 1 }
countByMap([{ count: 5 }, { count: 10 }, { count: 5 }], x => x.count);
// Map(2) { 5 => 2, 10 => 1 }

Context

From 30-seconds-of-code: count-grouped-elements

Revisions (0)

No revisions yet.