How can I group and count values in a JavaScript array?
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.
Count the occurrences of each value in an array
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.
const countOccurrences = array =>
array.reduce((accumulator, currentValue) => {
accumulator[currentValue] = (accumulator[currentValue] ?? 0) + 1;
return accumulator;
},
{});
console.log(countOccurrences(['a', 'b', 'a', 'c', 'a', 'a', 'b']));
// Output: { a: 4, b: 2, c: 1 }
console.log(countOccurrences([...'ball']));
// Output: { b: 1, a: 1, l: 2 }
Code Breakdown:
arr.reduce()
: This method iterates over the array, applying a function to each element and accumulating a result, which is an object in this case.a[v] = (a[v] ?? 0) + 1;
:a[v]
: This is the key in the objecta
corresponding to the valuev
in the array.?? 0
: The nullish coalescing operator checks ifa[v]
isnull
orundefined
. If it is, it initializes it to0
. Otherwise, it keeps its current value.+ 1
: The value is then incremented by 1, counting the occurrence.
return a;
: The final result is the objecta
that has all unique array elements as keys with their corresponding counts as values.frequencies([...array])
: This function can be applied to any array to get the frequency of each element in that array.
Example Usage:
frequencies(['a', 'b', 'a', 'c', 'a', 'a', 'b']);
- The result will be
{ a: 4, b: 2, c: 1 }
because ‘a’ appears 4 times, ‘b’ appears 2 times, and ‘c’ appears once.
- The result will be
frequencies([...'ball']);
- This spreads the string “ball” into an array
['b', 'a', 'l', 'l']
. - The result will be
{ b: 1, a: 1, l: 2 }
because ‘b’ and ‘a’ appear once, and ‘l’ appears twice.
- This spreads the string “ball” into an array
This function is a concise way to count occurrences of elements in an array, leveraging modern JavaScript features like reduce()
and the nullish coalescing operator.
Group the elements of an array based on a function
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.
const countBy = (array, iteratee) =>
array
.map(item => typeof iteratee === 'function' ? iteratee(item) : item[iteratee])
.reduce((accumulator, value) => {
accumulator[value] = (accumulator[value] || 0) + 1;
return accumulator;
}, {});
// Example usage:
console.log(countBy([6.1, 4.2, 6.3], Math.floor));
// Output: { 4: 1, 6: 2 }
console.log(countBy(['one', 'two', 'three'], 'length'));
// Output: { 3: 2, 5: 1 }
console.log(countBy([{ count: 5 }, { count: 10 }, { count: 5 }], x => x.count));
// Output: { 5: 2, 10: 1 }
Explanation:
1. Function Name and Parameters:
countBy
: The function is namedcountBy
to indicate that it counts items in an array based on a specific criterion.array
: This is the input array whose items you want to count.iteratee
: This is either a function or a string representing a property name. It determines how the items in the array should be transformed before counting.
2. Mapping the Array:
map(item => typeof iteratee === 'function' ? iteratee(item) : item[iteratee])
:- If
iteratee
is a function, it is applied to each item in the array to transform it. - If
iteratee
is a string, it is treated as a property name, and the corresponding property value is extracted from each item.
- If
3. Reducing the Array:
reduce((accumulator, value) => { ... })
:accumulator[value] = (accumulator[value] || 0) + 1;
: This line counts how many times each transformed value appears in the array.- If the value has already been encountered, it increments the count. If not, it initializes the count to 1.
- The final result is an object (
accumulator
) that contains the counts of each unique transformed value.
4. Example Usage:
countBy([6.1, 4.2, 6.3], Math.floor);
- This uses
Math.floor
to round down each number in the array, resulting in{ 4: 1, 6: 2 }
.
- This uses
countBy(['one', 'two', 'three'], 'length');
- This counts the number of strings with the same length, resulting in
{ 3: 2, 5: 1 }
.
- This counts the number of strings with the same length, resulting in
countBy([{ count: 5 }, { count: 10 }, { count: 5 }], x => x.count);
- This counts the occurrences of the
count
property, resulting in{ 5: 2, 10: 1 }
.
- This counts the occurrences of the
Using a Map
instead of an object
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.
// Function to calculate the frequency of elements in an array
const frequenciesMap = (arr) => {
return arr.reduce((map, value) => {
map.set(value, (map.get(value) ?? 0) + 1);
return map;
},
new Map());
};
// Example usage of frequenciesMap
const frequencyResult = frequenciesMap(['a', 'b', 'a', 'c', 'a', 'a', 'b']);
console.log(frequencyResult); // Output: Map(3) { 'a' => 4, 'b' => 2, 'c' => 1 }
// Function to count elements based on a given function or property
const countByMap = (arr, fn) => {
return arr
.map(typeof fn === 'function' ? fn : value => value[fn])
.reduce((map, value) => {
map.set(value, (map.get(value) || 0) + 1);
return map;
}, new Map());
};
// Example usage of countByMap
const countByFloor = countByMap([6.1, 4.2, 6.3], Math.floor);
console.log(countByFloor); // Output: Map(2) { 6 => 2, 4 => 1 }
const countByLength = countByMap(['one', 'two', 'three'], 'length');
console.log(countByLength); // Output: Map(2) { 3 => 2, 5 => 1 }
const countByCount = countByMap([{ count: 5 }, { count: 10 }, { count: 5 }], item => item.count);
console.log(countByCount); // Output: Map(2) { 5 => 2, 10 => 1 }
0 Comments