Spread the love

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.

tropical waterfall 1200

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:

  1. 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.
  2. a[v] = (a[v] ?? 0) + 1;:
    • a[v]: This is the key in the object a corresponding to the value v in the array.
    • ?? 0: The nullish coalescing operator checks if a[v] is null or undefined. If it is, it initializes it to 0. Otherwise, it keeps its current value.
    • + 1: The value is then incremented by 1, counting the occurrence.
  3. return a;: The final result is the object a that has all unique array elements as keys with their corresponding counts as values.
  4. 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.
  • 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 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 named countBy 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.

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 }.
  • countBy(['one', 'two', 'three'], 'length');
    • This counts the number of strings with the same length, resulting in { 3: 2, 5: 1 }.
  • countBy([{ count: 5 }, { count: 10 }, { count: 5 }], x => x.count);
    • This counts the occurrences of the count property, resulting in { 5: 2, 10: 1 }.

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 }

To efficiently manage and update the frequencies of elements, you can design a custom data structure known as a frequency map. This data structure will allow you to quickly check, increment, and decrement the frequency of values. Here’s a step-by-step guide to implementing this frequency map:

Frequency Map Data Structure

Overview

A frequency map is essentially a specialized dictionary where the keys are the elements whose frequencies are being tracked, and the values are the counts of those elements.

Features

  1. Add Element: Increment the frequency of an element.
  2. Remove Element: Decrement the frequency of an element.
  3. Get Frequency: Retrieve the current frequency of an element.
  4. Check Existence: Determine if an element is present in the map.

class FrequencyMap extends Map {
constructor(iterable) {
super();
if (iterable) {
for (const value of iterable) {
this.increment(value);
}
}
}
get(value) {
return super.get(value) ?? 0;
}
has(value) {
return this.get(value) > 0;
}
increment(value) {
super.set(value, this.get(value) + 1);
return this;
}
decrement(value) {
const currentValue = this.get(value);
if (currentValue > 0) {
super.set(value, currentValue - 1);
}
return this;
}
toSortedArray(ascending = true) {
const sortedEntries = [...this].sort((a, b) => {
const comparison = a[1] - b[1];
return ascending ? comparison : -comparison;
});
return sortedEntries.map(entry => entry[0]);
}
}
// Example usage
const fMap = new FrequencyMap(['a', 'b', 'c', 'a', 'a', 'b']);
fMap.decrement('c')
fMap.increment('d');
console.log(fMap.toSortedArray(false)); // [ 'a', 'b', 'd' ]

Key Changes:

  1. Initialization Check: Added a check to ensure the iterable is defined before using it.
  2. Decrement Logic: Simplified the decrement method to avoid negative values and ensure values are properly handled.
  3. Sorting Function: Improved the toSortedArray method to handle ascending and descending sorting based on the ascending parameter.
  4. Code Cleanliness: Improved readability and consistency in code style.

techbloggerworld.com

Nagendra Kumar Sharma I Am Software engineer

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *