.reduce method in IAS - Wherefore Art Thou

Okay, I spent something like 2 days staring at this and wondering what the heck was going on, but I think I’ve cracked it.

When I solve a challenge, I like to look at the other solutions to see the different ways it can be done. The solution with the .reduce method confused the @#$% out of me.

function whatIsInAName(collection, source) {
  const sourceKeys = Object.keys(source);
  return collection.filter(obj => sourceKeys
      .map(key => obj.hasOwnProperty(key) && obj[key] === source[key])
      .reduce((a, b) => a && b));
  [ { first: "Romeo", last: "Montague" },
    { first: "Mercutio", last: null },
    { first: "Tybalt", last: "Capulet" } ],  { last: "Capulet" }

In case anyone else had the same issue as me, I was reading the return statement too literally… left-to-right… and wondering how the hell you could filter collection by applying the map method to sourceKeys (which in the displayed example was simply { 'last' } ) using a Boolean test, before taking that shallow array and applying the reduce method with a crazy (accumulator, currentValue) test accumulator (a) && currentValue (b) :exploding_head: :exploding_head: :exploding_head: :exploding_head:

I feel a bit silly now that I’ve worked it out (I think), but when I read it right-to-left - and pay closer attention to the brackets () :person_facepalming: , the reduce method is applied to the mapped array containing the Boolean values and checks if all of the values are the same. For example

[ true, true, true ].reduce((a, b) => a && b) // returns true


[ true, true, false ].reduce((a, b) => a && b) // returns false

This result then comes back to the filter method being applied to collection and when true the obj being checked is included. When false it is filtered out.

So then if I get a bit pseudo-codey on the function with the Shakespearian names

// Object.keys(source); // output is { 'last' }
// Applied to each `obj` in `collection`
{ first: "Romeo", last: "Montague" }
sourceKeys.map('last' => obj.hasOwnProperty.('last') // true
&& obj['last' (Montague)] === source['last' (Capulet)] // false

So the { first: "Romeo", last: "Montague" } object is filtered out.

Ditto the unfortunate { first: "Mercutio", last: null } who has no last name and who also died half way through the play.

Juliet’s cousin { first: "Tybalt", last: "Capulet" } on the other hand…

sourceKeys.map('last' => obj.hasOwnProperty.('last') // true
&& obj['last' (Capulet)] === source['last' (Capulet)] // true

He too was killed off by Shakespeare mid-play, but he’d be happy to know that he was the only one to survive the collection filtering process which returns that single object

{ first: "Tybalt", last: "Capulet" } 

You’d think our friend, the Bard, would’ve been a fan of Game Of Thrones…

And if we were filtering collection based on the survival rate of these characters the following would be returned


.reduce((a, b) => a && b))

Honestly, naming those arguments “a” and “b” makes it much more confusing!