Patrick Loorbach

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.