Help with wherefore arr thou

Tell us what’s happening:
I am trying to solve this problem. To do this I have to check if the second argument corresponds to one of the three objects of the first argument of our function.
If I understand well the instructions, It must be the third object that must be returned.
{ first: "Tybalt", last: "Capulet" }
I am trying to do it with the classic for loop. But it doesn’t work for the moment. Can someone tell me what is wrong with this code below ?

  **Your code so far**

function whatIsInAName(collection, source) {
var arr = [];
// Only change code below this line
for (let i = 0; i < collection.length; i++) {
for (let j = 0; j < source.length; j++) {
  if (collection[i] === source[i]) {
    arr.push(collection[i]);
  }
}
}
// Only change code above this line
return arr;
}

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


  **Your browser information:**

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36.

Challenge: Wherefore art thou

Link to the challenge:

There are a couple of issues here:

First of all, what does this mean:

for (let j = 0; j < source.length; j++) {

source is an object, yes?

Secondly, even if it was an array, this

  if (collection[i] === source[i]) {

is checking if the entire object matches - you don’t want that.

So, for this:

whatIsInAName(
  [{ apple: 1, bat: 2 }, { apple: 1 }, { apple: 1, bat: 2, cookie: 2 }],
  { apple: 1, cookie: 2 }
);

it is asking you to return any elements from the first array whose values match those in the second parameter. It is not asking for an exact match. For example, it wants the last element in the array because it has keys of “apple” and “cookie” whose values match those - it doesn’t matter if there are other props in there. Maybe you can think of it as a subset, if that helps.

So, I think you need to rethink your algorithm. See what you can come up with.

1 Like

I had taken a break to come back better.
I thought about the explanation you gave me.

It’s was mistake of mine to try to iterate on an object, I thought it was an array (2nd parameter), I did not pay attention.

I thought about what you said and I come up with this but it still didn’t work.

function whatIsInAName(collection, source) {

  var arr = [];

  // Only change code below this line

for (let i = 0; i < collection.length; i++) {

  if (collection[i].hasOwnProperty(source)) {

    arr.push(collection[i]);

  }

}

  // Only change code above this line

  return arr;

}
  if (collection[i].hasOwnProperty(source)) {

but source is not a string, it’s an object.

I still think you need an inner loop there. But you just need to loop over source just not in the way you were thinking. And within that loop, what are you going to do?

I think it is worthwhile explaining to yourself (or a rubber duck) what the steps are, you can even write it out in pseudo code. Let me just run through it verbally.

We are going to loop through collection. On that first iteration, we get the first element, { apple: 1, bat: 2 }. Now, we need to go through the key/value pairs in source: { apple: 1, cookie: 2 }. First we need to check if the element in the outer loop as a prop named “apple” whose value is “1”, and then a prop called “cookie” that has a value of “2”. If both those are true, we have a match.

Does that makes sense?

1 Like

Yes, it makes sense. But I don’t know why I’m still stuck in this exercise. Maybe the for loop is not as simple as some people think. As far as I’m concerned (as a beginner in js) things are always complicated with for loop when you have to put outer loop and inner loop. It is not hard to iterate, but the problem comes once you have to write the code in your scoop.

The challenge in question, as you explained it well I have to iterate in the first parameter which contains an array and in this array there are objects. I come with up this.

for (let i = 0; i < collection.length; i++) {
  arr.push(collection[i]);

First iteration, I normally got this, rigth ? :
{ first: "Romeo", last: "Montague" }
Second iteration:
{ first: "Mercutio", last: null }
Third iteration:
first: "Tybalt", last: "Capulet" }

I don’t know if I have to push the results that I had in the array that I created at the beginning and then I create my inner loop (I don’t know)
I doubt it, because I got the whole array when I do this:

[{premier: 'Romeo', dernier: 'Montague'},
  {first: 'Mercutio', last: null},
  {premier: 'Tybalt', dernier: 'Capulet'}]

I also have a real problem to iterate in my inner loop, since the second parameter is an object. It’s not an array. How to go throught source as it is an object that contains key/value pairs. The real problem for me is how to GO THROUGTH THE KEY/VALUE PAIRS.

for (let i = 0; i < collection.length; i++) {
  arr.push(collection[i]);

That iteration makes sense, but I would expect that push to be conditional.

I don’t know if I have to push the results that I had in the array that I created at the beginning and then I create my inner loop (I don’t know)

Again, I would expect an outer loop to go through the first parameter. Then inside that I would expect you to loop though the key/value pairs of the second parameter. Only if all of the key/value pairs are in the current element from the outer loop, then and only then would you push that outer loop element onto for solution array.

[{premier: 'Romeo', dernier: 'Montague'},
  {first: 'Mercutio', last: null},
  {premier: 'Tybalt', dernier: 'Capulet'}]

I don’t understand why there is English and French mixed up here.

I also have a real problem to iterate in my inner loop, since the second parameter is an object. It’s not an array

Yup. Is there a way to loop through an object? That is the big question. I would suggest that you do a google search for “javascript loop object” and you will find a few different options. Seriously, being able to google things is one of the most important skills a developer can have. That being said, these topics have been discussed by FCC, here. And this could hint at a different approach.

It’s not a problem that you don’t remember those - we’re human - but it is good to get good at googling things.

1 Like

You are right, there are challenges where we have been taught how to iterate on object. I had completely forgotten that. Yes, we can google specific questions (e.g. how to iterate on object) instead of bothering you with really dumb questions (my bad).

Sometimes I translate the pages of my browser into French (native language) if I don’t understand the instructions of the challenge very well. (translation problem on the output)

Let’s go back to the challenge.
I iterated on the array. Then I iterated over the object.
And then I checked if one of the objects in the collection has a niceName property and one of the objects in the collection is strictly equal to the source object. But again it didn’t work

function whatIsInAName(collection, source) {

  var arr = [];

  // Only change code below this line

  for (var i = 0; i < collection.length; i++) {

    for (var niceName in source) {

      if (collection[i].hasOwnProperty(niceName) && collection[i] === source[niceName]) {

        arr.push(collection[i]);

      }

    }

  }

  // Only change code above this line

  return arr;

}

That’s cool, I was just worried that you were trying to run that code.

As to why it isn’t working, you have to figure it out. “it still didn’t work” isn’t good enough. How isn’t it working. There are an infinite number of ways it could be “not working”. We need to narrow that down.

One way is to try to see what is happening internally in your code. I would do something like this:

function whatIsInAName(collection, source) {
  var arr = [];
  // Only change code below this line

  for (var i = 0; i < collection.length; i++) {
    console.log('outer loop', collection[i]);
    for (var niceName in source) {
      console.log(`inner loop {${niceName}: ${source[niceName]}}` );
      console.log(`checking collection[i].hasOwnProperty(niceName) (${collection[i].hasOwnProperty(niceName)}) && collection[i] === source[niceName] (${collection[i] === source[niceName]})`);
      if (collection[i].hasOwnProperty(niceName) && collection[i] === source[niceName]) {
        console.log('*** match found, pushing', collection[i])
        arr.push(collection[i]);
      }
    }
  }
  // Only change code above this line
  return arr;
}

And take a look at what console logs are saying is happening.

1 Like

But I will say that the basic looping structure that you have makes sense to me. It’s just the logic of when to push, I think.

1 Like

You are right dear. This time I am not coming to say “it didn’t work”. I think it must work but I just can’t pass the challenge.
I came up with this:

function whatIsInAName(collection, source) {

  var arr = [];

  // Only change code below this line

  for (var i = 0; i < collection.length; i++) {

    for (var niceName in source) {

      if (collection[i].hasOwnProperty(niceName) && collection[i][niceName] === source[niceName]) {

        arr.push(collection[i]);

      }

    }

  }

  // Only change code above this line

  return arr;

}

And the result we expected is this:
[{ first: "Tybalt", last: "Capulet" }]

Ok, That’s the result we wanted, if I can’t pass the challenge it’s maybe a bug. Or maybe that’s not good enough to pass the challenge.

But you need to figure out why it is failing. You haven’t answered that question. Look at a failing test:

whatIsInAName([{ "apple": 1, "bat": 2 }, { "bat": 2 }, { "apple": 1, "bat": 2, "cookie": 2 }], { "apple": 1, "bat": 2 }) should return [{ "apple": 1, "bat": 2 }, { "apple": 1, "bat": 2, "cookie": 2 }] .

So, it is expecting:

[
  { apple: 1, bat: 2 }, 
  { apple: 1, bat: 2, cookie: 2 }
]

but you are returning:

[ 
  { apple: 1, bat: 2 },
  { apple: 1, bat: 2 },
  { bat: 2 },
  { apple: 1, bat: 2, cookie: 2 },
  { apple: 1, bat: 2, cookie: 2 } 
]

I suggested a way to debug this, see what you can come up with.

1 Like

After hours spent in this challenge, I come up with this. Maybe it can be improved because it seems long and confusing. Thanks for your help

function whatIsInAName(collection, source) {

  var arr = [];

  // Only change code below this line

  var props = Object.keys(source);

  

  for (var i = 0; i < props.length; i++) {

  for (var j = 0; j < collection.length; j++) {

    if (!collection[j].hasOwnProperty(props[i]) || collection[j][props[i]] != source[props[i]]) {

      collection.splice(j, 1);

      if (collection.length > 0) {

      j --;

      }

    }

  }

}

  arr = collection;

  // Only change code above this line

  return arr;

}
1 Like

Cool, that looks like a good solution. Good job.

If I had to critique it…

You’ve kind of chose the opposite approaches of what I would have done - first your loops are the opposite of what would be “obvious” to me - I would iterate through collection on the outside, but that’s probably mostly preference.

The other thing is that because you are removing bad items from collection (instead of adding good ones to arr) you are manipulating the length of collections while you are inside a loop that depends on that length. Now, your j--; compensates for that, but it takes some thought - my philosophy is that the quicker it is to read code the better.

The last thing is that you are mutating an input parameter. There is a principle in function programming that we keep our functions pure. One of the rules for purity is that we don’t have side effects outside of the function. This is mutating the array and since arrays are reference variables and since passing a reference variable doesn’t make a new copy but simply copies the reference, any change you make to collection here will also change the variable that was passed in. (Yes, that’s confusing at first.) But that would be easy to fix. Instead of var arr = [];, we could do var arr = [...collection]; or var arr = collection.slice(); This would create a shallow copy so you won’t affect the original. If you’re worried about coding above the “Only change code below this line” comment, you could just put arr = [...collection]; on the line below.

But still, good work. This is hard stuff, be proud. Have fun on the next one.

2 Likes

Thank you very much!
After reading your explanation, I’ve decided to make some changes on what I did just before.

To do this, I googled some stuff, I took all the solution proposals I made since the beginning, and I just added an initializer (theNumberOfMatches = 0)

1 - As you suggested, I iterated on collection
for (let i = 0; i < collection.length; i++){ }

2- I’ve decided also to not "removing bad items from collection , I’m adding good ones to arr " )
Is it quicker to read ? for me yes

function whatIsInAName(collection, source) {

  let arr = [];

  // Only change code above this line

  //arr = [...collection];

  let theNumberOfMatches = 0;  

  let props = Object.keys(source);

  for (let i = 0; i < collection.length; i++){

    theNumberOfMatches = 0;

        for (let j in source) {

      if ((collection[i].hasOwnProperty(j)) && (collection[i][j] == source[j])){

        theNumberOfMatches++;

      }

    }

     if (theNumberOfMatches == props.length){

       arr.push(collection[i]);

    }

  }

  // Only change code above this line

  return arr;

}

3- The last thing to do is to keep my function pure, not to mutate my array.
I tried several times, I failed. I tried to see why I failed several times to not be able to mutate my array, I failed.
I did it like this:

function whatIsInAName(collection, source) {

  let arr = [];

  // Only change code above this line

  arr = [...collection];

  let theNumberOfMatches = 0;  

  let props = Object.keys(source);

  for (let i = 0; i < collection.length; i++){

    theNumberOfMatches = 0;

        for (let j in source) {

      if ((collection[i].hasOwnProperty(j)) && (collection[i][j] == source[j])){

        theNumberOfMatches++;

      }

    }

     if (theNumberOfMatches == props.length){

       arr = collection[i];

    }

  }

  // Only change code above this line

  return arr;

}

I also brought back my spread method which allows me not to mutate the original array in my last if statement.
Like this:


if (theNumberOfMatches == props.length){

       arr = [...collection];

       arr = arr[i]

    }

What if my original array was not mutated?

With regards to point 1, it’s not that big of a deal, it works either way, it just seems a little odd to me, and I expect most people would instinctively do it the other way. But again, don’t worry about it.

For the second point, it’s about readability. Most people instinctively expect a for loop to increment or decrement through its control variables. If I see:

for (var j = 0; j < collection.length; j++) {

I assume that we are going to be iterating though that collection. Most people would assume that that the length of collection is fixed and that we are just going to increment the control variable. (This is one of the reason I like prototype methods - the iteration is internalized and can’t be messed with.) But you are changing both of those as you go. Sure, it makes sense to you right now, but we should be concerned about how it looks to someone else. That includes you in 2 months, because you will not remember the logic of it and you will be in the same boat as the rest of us.

A lines of code are monumentally worth an increase in readability and making code more “obvious”. If you don’t want to take my word for it, that’s fine, you’ll just end up learning the hard way.

As to the third point:

  arr = [...collection];

This is not needed. Since we will be adding good members, we actually wanted to start with an empty array. This was only necessary if we were subtracting from the original list. But we are leaving the original list alone.

Also this line

  arr = [...collection];

Yes, that will work. But the point was that you could do that and then use arr instead of collection. That way, when you do splice it mutates the copy, not the original. But if you aren’t going to mutate the original then there is no point.


With this last code (listed under item 3), you’ve kind of combined different approaches.

To fix the code you have, I had to make two changes.

This:

  arr = [...collection];

is not needed since we want that array empty so we can add good elements to it. This was only necessary if we were going to mutate the original. We want this to start out as an empty array.

And this:

       arr = collection[i];

does not do what we want. We want to add that element to the array. This completely overwrites the array with that element.

When I fix those two, the code passes for me.


And look, I’m not saying you need to obsess about this challenge. I was just pointing some things out. I suggest moving on to the next challenge. If you really want to learn about this algorithm, then I’d look up what some other people have done on this. You can search the internet, sometimes people solve it on youtube. And of course in the FCC page for this, there is the Get Help -> Get a Hint button where people have often submitted other solutions. Take a peak a see what they did - it can be very instructive.

1 Like

i see you take great interest in different solutions of this challenge so i wrote a quick one utilizing basic for loops and not taking advantage of advanced methods like filter/map. I find it important as a newbie coder to stick to basic solutions for a while, which can help you understand better built in methods later.

function whatIsInAName(collection, source) {
  var arr = [];
  // Only change code below this line

  for (let i=0; i<collection.length; i++){
    let keys=0
    for (let key in source){
      keys++
      if (collection[i][key]){
        if (collection[i][key]===source[key]){
          keys--
        }
      }
    }
    if (keys===0) arr.push(collection[i])
  }

  // Only change code above this line
  return arr;
}

Ill narrate it a bit, pinpointing variances with corresponding lines of your first example solution.
Instead of using Object.keys(source) to retrieve the number of keys, i store them in the keys variable, which is incrementing on every for key in source loop. I initiate it with zero value for every object in the collection array and decrement it whenever the objects have a matching key/value pair. If all key/value pairs in source are matched with the respective object, the keys value will remain zero and i push the object into the result arr. The for (let key in source) loop lets me access the string representation of each key directly, instead of using the hasOwnProperty method. In fact, you already have the list of keys in your props var, so you could use those too.
Your theNumberOfMatches has similar purpose to the keys var i use. You could also initiate it directly inside your first for loop, instead of declare it outside and then reset it to zero on each iteration(it would save a line)(its used only inside the loop, so you dont need to declare it outside, you could even declare it along the ‘i’ variable inside the for loop statement).

1 Like

I’ve added spoiler tags around the working solutions
[spoiler] and [/spoiler]

2 Likes

Thank you very much
I understand everything you said about a readable code. Of course I take into account your suggestions, and I appreciate very much your advice especially to make a code readable and also not to mutate an origin array.

Yes I saw the proposed solutions, and I like the one with the filter method, simpler, shorter.

I already passed the challenge and started another one. I just wanted to do it with for loop

1 Like

thanks. i actually was rushing back to add spoilers myself

2 Likes

Oh dear, thank you very much

This solution is similar to mine but much simpler. That’s the for loop I like.
I understand every line of the code and it is very readable for me.
Thank you very much for thinking of this.