In the back story why I started this series, I wrote that a particular example in the MDN doc confused me more. It led me to posting my question in my mentor's Slack channel. The elucidating conversation with another student there made me better grok reduce() so that the series was born.

That example is the one we're looking at today. Given an array of items, say of names, a lot of which might be same/duplicated, how do you see how many times each identical name occurs? Specifically, how do you produce an object of these occurences showing the counts alongside the name, a key-value pair matching?

With a helper method like we'll have here, practically, we'll get a way to tell, say, how many students are named "Alice" in a class.

May I interrupt for a sec and mention here that if you're serious about mastering proper JavaScript, consider joining my mentor's system at the Vanilla JS Guides. Purchasing the guides gives you exclusive access to the Slack channel I've boasted about a lot, because so good!

Counting instances of values in an array

Say you have an array of a class' students' names, like so

    var names = [
        'Alice',
        'Loisa',
        'Bethany',
        'Rob',
        'Alice',
        'Choice',
        'Bob',
        'Rob',
        'Bruce',
        'Kingsley',
        'Beth',
        'John',
        'Joe',
        'Brent',
        'Kingsley'
    ]

OK, so that's totally made up - your class got cooler student names than those. And if you're Alice, let's see how many more students like you bear the name.

    var
        setCount = (list, i) => {
            (i in list) ? list[i]++ : (list[i] = 1);
            return list;
        },
        ini = {},
        occurences = arr => arr.reduce(setCount, ini) // our count of occurences helper
    ;

    occurences(names);
    /**
        {
            Alice: 2,
            Beth: 1,
            Bethany: 1,
            Bob: 1,
            Brent: 1,
            Bruce: 1,
            Choice: 1,
            Joe: 1,
            John: 1,
            Kingsley: 2,
            Loisa: 1,
            Rob: 2
        }
    * /

Because it's good practice to define your reduce() function away first and call it in later, I've defined setCount() so. setCount() is, hopefully, easier to read as "check the list for indices". Is there an index in the list already, then increment the index, else set the index count/value to 1.

So, what is happening in occurences()

In might help you fully get it if took 2 minutes to review how reduce() works, just in case. But if you're with me, here's what's happening.

As you call occurences() against names, reduce() runs the setCount() callback using {}, empty object as the accumulator, since that was given as initial value. On each iteration, starting from index 0, everything else depends on the operations defined in setCount().

JavaScript knows what to do with setCount() on each call, right. It takes names to be the list required argument and thus runs the object for in loop. At the end setCount, returns the list, the accumulator, which now has our occurences data.

Quick notes

In case you're confused,

  1. reduce() signature:

         arr.reduce(callback, ini);

    compares with

        occurences = arr => arr.reduce(setCount, ini)

    where setCount is our callback, and

  2. what is expected of reduce()'s callback

        var callback = (acc, cur) => acc;

    compares with

        var setCount = (list, i) => {
            (i in list) ? list[i]++ : (list[i] = 1);
            return list;
        }

I especially got confused at the bracket notation usage in setCount(). I forgot that when you're trying to access property in an object with a variable, then you must use the bracket notation. Here's where Kieran Barker, fellow mentee at #vanillajs, came in and unstuck me. Really, you'd get an "Uncaught TypeError" should you write setCount with dot notation, which should point you in the right direction anyway.

In closing, know that, just like with everything reduce() and all the new array methods, the magic happens in the callback function. All setCount is doing is assigning a value of 1 to each name in names, meaning that if a name occurs at least once in names, it's, obviously, 1. Of course, if more than once, then increment its occurrence accordingly.

So, now we have a new helper function - occurences() - that helps us check the count of occurence(s) of an item(s) in a list. Cool.