Explain Codes LogoExplain Codes Logo

Counting the occurrences / frequency of array elements

javascript
data-processing
frequency-counting
map-object
Anton ShumikhinbyAnton Shumikhin·Sep 13, 2024
TLDR

A reduce function is a reliable tool for dynamically tallying elements in arrays. Here, an object count gets populated with keys (unique array elements) and values (their occurrence count):

const fruits = ['apple', 'banana', 'apple']; const count = fruits.reduce((tally, fruit) => { tally[fruit] = (tally[fruit] || 0) + 1; // Not just apples, increases bananas too 🍎🍌 return tally; }, {}); console.log(count); // { apple: 2, banana: 1 }

Getting your head around frequency count

In the realm of data processing and visualization, knowing how to count the frequency of array elements effectively is a game-changer. This section explores different variations, their practicality, and associated gotchas.

Using Map for cleaner, modern counting

With a Map object, you can achieve clean code with even cleaner access mechanisms:

const fruits = ['apple', 'banana', 'apple']; const count = new Map(); // A Map is not the territory. Or is it? 🤔 fruits.forEach(fruit => count.set(fruit, (count.get(fruit) || 0) + 1)); for (const [key, value] of count.entries()) { console.log(`${key}: ${value}`); // Here's where we spill the beans...or fruits }

Immutably count frequencies with reduce and spread

To keep the side effects at bay, clone your array with spread operator:

const fruits = ['apple', 'banana', 'apple']; const count = [...fruits].reduce((tally, fruit) => { tally[fruit] = (tally[fruit] || 0) + 1; // Keep calm and count on...🧮 return tally; }, {});

Count while strolling sorted array

Grouping identical elements together with sort before counting, all for easier logic and possible time complexity reduction:

const sortedFruits = ['apple', 'apple', 'banana'].sort(); let previousFruit, frequency = 1; // Ah, our first fruit to count. for (let i = 1; i <= sortedFruits.length; i++) { if (sortedFruits[i] === previousFruit) { frequency++; // Same fruit? Increase the count. } else { if (previousFruit !== undefined) { console.log(`${previousFruit}: ${frequency}`); // Hello, we've got a count for you! } previousFruit = sortedFruits[i]; frequency = 1; // Reset count for the new fruit. } }

Let libraries lend a helping hand

With lodash or underscore we're in expressive, low boilerplate territory. Here, we are using lodash's _.countBy:

const fruits = ['apple', 'banana', 'apple']; const count = _.countBy(fruits); // This function never goes bananas... it just counts them! console.log(count); // { apple: 2, banana: 1 }

Tackling the complex cases

When your arrays contain more than just primitive values and start venturing into complex territories (objects or arrays), you need to up your frequency counter game:

Good old JSON for complex keys

Use JSON.stringify() to convert complex elements to a string:

const arrOfObjects = [{ id: 1 }, { id: 1 }, { id: 2 }]; const count = arrOfObjects.reduce((tally, obj) => { const key = JSON.stringify(obj); // Happy stringification! 🎁 tally[key] = (tally[key] || 0) + 1; return tally; }, {}); console.log(count); // {"{\"id\":1}": 2, "{\"id\":2}": 1}

The Map of complex keys

For fans of the Map object:

const arrOfObjects = [{ id: 1 }, { id: 1 }, { id: 2 }]; const count = new Map(); // Behold the map! Like Dora's but for complex keys arrOfObjects.forEach(obj => { const key = obj.id; // Key so simple, even a hobbit could handle it count.set(key, (count.get(key) || 0) + 1); }); for (const [key, value] of count) { console.log(`${JSON.stringify(key)}: ${value}`); // Prepare for key:value nirvana }

The world beyond counting frequencies

Once you have your frequency object or Map, there are different ways to leverage it:

Scooping unique values

Dipping into the object for a dose of unique elements using Object.keys():

const uniqueFruits = Object.keys(count); // Who's unique? You'll find them here! console.log(uniqueFruits); // ['apple', 'banana']

Ordering by frequency

To reveal the most frequent elements, just sort them:

const sortedFrequencies = Object.entries(count).sort((a, b) => b[1] - a[1]); // It's a bird, it's a plane, no, it's sorted frequencies! console.log(sortedFrequencies); // [['apple', 2], ['banana', 1]]

Modern iteration with for...of

Use modern syntax for iteration:

for (const [fruit, frequency] of Object.entries(count)) { console.log(`${fruit}: ${frequency}`); // Fruity frequencies, fresh from the oven! }