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));
}
whatIsInAName(
[ { 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)
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 () , 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
Whereas
[ 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
[]