Declarative Duplicates
Let’s say we have an array of numbers, and we want to count how many times each number appears. The way I used to approach this was using a for
loop, iterating over each element and keeping count of them in an object. I recently learned this is called an imperative (describe how to do something) approach, and would look something like this:
// Imperative approach (for loop)
const arr = [1, 2, 3, 2, 1, 4, 1];
const countObj = {};
const test = (arr) => {
for (let i = 0; i < arr.length; i++) {
if (countObj[arr[i]]) {
countObj[arr[i]] = countObj[arr[i]] + 1;
} else {
countObj[arr[i]] = 1;
}
}
return countObj;
};
console.log(countObj);
// { '1': 3, '2': 2, '3': 1, '4': 1 }
This is a good approach, but you can also consider using a declarative approach (describe what needs to be done). I’ve found the following code block to be a much cleaner way of approaching this problem:
// Declarative approach (reduce)
const arr = [1, 2, 3, 2, 1, 4, 1];
const countObj = arr.reduce((acc, el) => {
acc[el] = (acc[el] || 0) + 1;
return acc;
}, {});
console.log(countObj);
// { '1': 3, '2': 2, '3': 1, '4': 1 }
In this case, .reduce()
is used to iterate over the array, and it helps accumulate the object without having to manually set up a loop. The accumulator is initialized to an empty object ({}
) when .reduce()
starts, and on every iteration, the expression acc[el] = (acc[el] || 0) + 1
ensures that the element in the accumulator is counted correctly.
At first, I was wondering how this expression is able to check if the element exists in the object. I found that the Logical OR (||
) operator works by returning the first truthy value it encounters. You can check this by running these examples:
false || "hello"; // "hello" (since "hello" is truthy)
null || 42; // 42 (since 42 is truthy)
0 || "world"; // "world" (since "world" is truthy)
Since I had previously only used the Logical OR with Boolean values (like inside an if
statement), I assumed it would only return boolean values. But the fact is, the ||
operator actually returns the value of the first truthy operand when used with non-boolean values.
This means that during each iteration, acc[el] = (acc[el] || 0) + 1
checks if the current element (el
) exists in the object. If it does, it increments the count by 1. If it doesn’t exist, the expression acc[el] || 0
evaluates to 0
, and then 1
is added to that, effectively starting the count at 1.
Conclusion
This declarative approach, using .reduce()
, allows us to focus on what we want to achieve — counting occurrences — without worrying about how the iteration and accumulation happen. The ||
operator simplifies checking for existing properties in an object, making the code more elegant and readable. The resulting code seems simpler and cleaner, and I was very excited to find out about it.