Help understanding the codes in Intermediate Algorithm Scripting: Wherefore art thou

I’m trying to understand the advanced code solution for the Intermediate Algorithm Scripting: Wherefore art thou challenge, but I’m having some trouble, particularly with regards to the reduce and obj[key] === source[key] parts.

I tried using console.log (like console.log(obj[key]), but nothing is showing up even though this worked when I tried to console.log sourceKeys.

I wrote down the advanced code solution shown in the guide using arrow functions:

let whatIsInAName = (collection, source) => {
 let sourceKeys = Object.keys(source);
 return collection.filter(obj => sourceKeys.map(key => obj.hasOwnProperty(key) && obj[key] === source[key]).reduce((prev, curr) => prev && curr));
};

console.log(whatIsInAName([{ first: "Romeo", last: "Montague" }, { first: "Mercutio", last: null }, { first: "Tybalt", last: "Capulet" }], { last: "Capulet" }));

Then I tried to explain each piece of code:

First, use Object.keys to get the name/s of the objects in the source parameter and store the value in a variable. In this case, when you console.log sourceKeys, you get [“last”].

Next, use filter on the array of objects in the collection parameter. Filter will loop or iterate through each of these objects. The functions inside filter will then be applied to each of these objects. Filter will return a new array. This new array will be made up of the objects that pass the functions implemented by filter.

Inside filter, use map on sourceKeys. Map will loop or iterate through each of the elements or values in the sourceKeys array. Each time map runs, check if any of the objects in collection (obj) have the same value as in any of the property names in sourceKeys (key) - this corresponds to the obj.hasOwnProperty(key).

In this case, we’re checking if any of the objects in collection have a “last” property. If yes, hasOwnProperty will return true. Otherwise, it will return false. In the example given, there are 3 objects in the collection parameter:

{ first: "Romeo", last: "Montague" }

{ first: "Mercutio", last: null }

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

All of them have the “last” property, so hasOwnProperty will return true for all 3.

Is this right so far?

After that is where things get confusing for me. I’m not sure exactly what this code

obj[key] === source[key]

corresponds to. Using console.log doesn’t show me anything. Are we checking if the value of “last” in the object parameter is the same as the value of “last” in the source parameter?

So we’re looking to see which item in collection has this object: { last: “Capulet” }. Is that correct? But then I thought key was referring to just “last”. Does key also refer to the value of “last”? So, does it go like this:

{ first: "Romeo", last: "Montague" } === { last: "Capulet" }
The answer is false

{ first: "Mercutio", last: null } === { last: "Capulet" }
The answer is false

{ first: "Tybalt", last: "Capulet" } === { last: "Capulet" }
The answer is true

Is that right? Then after that, use the logical AND operator to compare the Boolean value we got above from the Boolean value we got from hasOwnProperty. The logical AND (&&) operator will only return true if both sides of the argument are true. If even one side of the argument turns out to be false, the whole thing will return false.

true && false = false

true && false = false

true && true = true

So map will return this array: [false, false, true]. Is this correct? Then we use reduce on this array, so we only get a single Boolean value. prev refers to the previous value in the array while curr refers to the current value in the array. We use the logical AND operator to compare the previous and current values. Since there’s no initial value, we start by comparing the first two values, right? So, that would be:

false && false = false

Then the value we got above becomes the new prev while the next value in the array becomes the new curr, right? So, we’ll get:

false && true = false

Is this correct? Am I supposed to be getting false here? This is also another part where I got confused as I’m not sure how the filter comes into play here.

I know filter is supposed to be filtering out the false values, returning a new array with only the elements that returned true, but I’m confused how this works here. If we end up with false, wouldn’t this be filtered out or removed from the array returned by filter?

Can anyone clarify what’s going on here? How does this code work?

obj[key] === source[key]

inside that piece of code, obj is each item in collection (the filter method iterates over collection calling the element that is being evaluated obj, and key comes from the map method applied on sourceKeys. in this case key is "last". So you have obj["last"] === source["last"].
obj["last"] evalutes to "Montague", null or "Capulet" depending on which element of the collection array is considered. source["last"] is always "Capulet"
obj["last"] === source["last"] will be true only for "Capulet" === "Capulet"
let’s remember that sourceKeys was originally ["last"], using map() it has become [true].Reduce is then applied to this new array, in this way it is checking if all properties in the source object are present in the evaluated object inside the collection property

I am rewriting the solution to show what is applied on what below. If you see you have collection.filter(), and then inside the filter method there is sourceKeys.map().reduce().
So, sourceKeys is an array of strings, map() makes it an array of boleans, reduce() makes the array of boleans in a sinlge bolean, and that bolean determinates if the element inside the collection array that is being evaluated will be kept or filtered out.

I am not sure how clear I was, ask again if you think I was not clear enough

let whatIsInAName = (collection, source) => {
    let sourceKeys = Object.keys(source);
    return collection.filter(
                     obj => sourceKeys.map(key => obj.hasOwnProperty(key) && obj[key] === source[key])
                                      .reduce((prev, curr) => prev && curr)
    );
};

console.log(whatIsInAName([{ first: "Romeo", last: "Montague" }, { first: "Mercutio", last: null }, { first: "Tybalt", last: "Capulet" }], { last: "Capulet" }));
1 Like

Thanks for your help :smiley:. I understand what this code obj[key] === source[key] means now. But I’m still having some trouble understanding how map, reduce, and filter work in this challenge. So for the given example, the code below will get us the following values:

obj.hasOwnProperty(key) && obj[key] === source[key]
true && Montague["last"] === Capulet["last"] //the answer is false
true && null["last"] === Capulet["last"] //the answer is false
true && Capulet["last"] === Capulet["last"] //the answer is true

Is this correct? Since we’re using map, a new array will be created with these Boolean values. So we’re getting [false, false, true], right? Then we use reduce on this array, so we only get one Boolean.

The reduce part of the code is this (prev, curr) => prev && curr). According to the documentation for reduce, if there’s no initial value given, the first element will be used. So prev refers to the previous value while curr refers to the current value in the array. So we’ll get:

false && false //returns false
//then, we use the answer from above as the new prev, right? And the new curr is true. So we get:
false && true //returns false

Is this correct? Should reduce be returning false? How would filter work in this situation then? According to the documentation on filter, filter is supposed to filter out or remove the values that don’t pass the test implemented by the provided function. Filter will then return an array made up of the elements that pass the test. So I’m not sure how filter works here?

1 Like

You are right on what the methods do, but it seems you are confused on what each method is called on
You can try to see it looking at the flow of the code using this tool: JavaScript Tutor (with already the code for this challenge)

Or let’s try to explain it step by step:

Click to show/hide

Let’s start with reminding what each variable is in this case:
collection is [{ first: "Romeo", last: "Montague" }, { first: "Mercutio", last: null }, { first: "Tybalt", last: "Capulet" }]
source is { last: "Capulet" }
and sourceKeys is ['last']

inside the function we have

return collection.filter(obj => sourceKeys.map(...).reduce(...))

or

return [{ first: "Romeo", last: "Montague" }, { first: "Mercutio", last: null }, { first: "Tybalt", last: "Capulet" }].filter(   obj => [last].map(...).reduce(...))

obj will be each element inside the array. The first time we have { first: "Romeo", last: "Montague" }.
Inside filter there is ["last"].map(key => obj.hasOwnProperty(key) && obj[key] === source[key]).reduce(...)
map we already know that with “last” in this case is false, so the next step is [false].reduce(…) which will result in false (just a bolean,no array). Resulting in this obj not being included in the filtered array

The same logic is applied to the other two elements inside collection
If you need I can try to explain it more…

Here’s how I see it. I hope this makes sense and is correct.

function whatIsInAName(collection, source) {
  let srcKeys = Object.keys(source); 
// Create an array of the source object's keys, which would be [last] in the example

  return collection.filter(function (obj){
    // Filter each object (obj) in the collection array. Filter must be passed a True/False value. 
    // Here is how to get True/False:
    return srcKeys
    .map(function(key){
    // Map srcKeys array to True/False values. Each srcKeys value is mapped to True IF:

      return obj.hasOwnProperty(key)      
      && obj[key] === source[key];
    // the source keys are contained within the object keys AND the values are the same.
    // else, the srcKeys value is mapped to False
    })

    // Now we have a whole array of True/False values. But collection.filter() only accepts one True/False value to decide on the whole object.

    // We can use reduce to flatten the True/False array into a single True/False value. We only want to      reduce to True if all the array values are True.

    .reduce(function(a,b) {
      return a && b;
    });
    // If the first and second values in srcKeys.map() are both True, return True. Reduce iterates through     the whole srcKeys array repeating this. 
    // Array will only reduce to True if at every part of the array, return a && b == True.
    // This reduced value is then passed to the filter, and the object is filtered accordingly.
  });
}

whatIsInAName([{ first: "Romeo", last: "Montague" }, { first: "Mercutio", last: null }, { first: "Tybalt", last: "Capulet" }], { last: "Capulet" });

So youre saying that:
sourceKeys.map(key=>key) means : that map is iterating over sourckeys so we get key =last
obj.hasOwnProperty(key) so: obj=collection have the property(last)
obj[key]===source[key] so: “Montague”,null,“Capulet” AND shoud be equal to “Capulet”
if everything I said was right can you please explain the reduce logic .
Thanks

reduce is acting on an array of booleans, and reducing it to a single boolean value