Some question JS

And again, keep at it, we’re making progress. This is how good coders are made. This stuff is hard. Everyone struggles with this stuff when they learn it the first time.

sorry for the mess in advance

function whatIsInAName(collection, source) {
  const arr = [];
  // Only change code below this line
  const sourceValues = Object.values(source)
  const sourceKeys = Object.keys(source);
  for (let i = 0; i < collection.length; i++) {
    const collectionValues = Object.values(collection[i])
    const objKeys = Object.keys(collection[i])
    for (let j = 0; j < objKeys.length; j++) {
      //(collection[i].hasOwnProperty(objKeys[j])) 
      for (let k = 0; k < sourceKeys.length; k++) {
        //source.hasOwnProperty(sourceKeys[k])
        for (let b = 0; b < collectionValues.length; b++) {
          for (let n = 0; n < sourceValues.length; n++) {
            if (collectionValues[b] == sourceValues[n] && collection[i].hasOwnProperty(objKeys[j]) == source.hasOwnProperty(sourceKeys[k])) {
              arr.push(collection[i])
            }
          }
        }
      }
    }
  }
  arr.pop()
  console.log(arr)
  // Only change code above this line
  return arr;
}

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

it consoles the correct answers yet it doesn’t pass… do you know why?
edit: I see whats going on with other inputs.

my main issue with this challenge is I feel my knowledge with objects a bit lacking.

my main issue with this challenge is I feel my knowledge with objects a bit lacking.

I disagree. I think the issue isn’t your JS, it is your understanding of the algorithm. I feel like you are trying to get you to think about the algorithm in isolation, in pseudo code. Or even in paragraph form. Just isolate what you are trying to do. I feel like you are trying to code it before you understand the approach.

So, you have this going on:

loop collection
  loop collection object keys
    loop source keys
      loop collection object values
        loop source values

First of all, any time you have nested loops 5 layers deep, something has usually gone wrong. But why do we want to try every possible combination of collection values and every possible collection key? Don’t we only care about the ones that are paired? The same goes for the source.

Reread the instructions:

Make a function that looks through an array of objects (first argument) and returns an array of all objects that have matching name and value pairs (second argument).

Seriously, did you read it? I’m not trying to be glib here - I do this all the time. Reread the requirements. When given a task at work, I often read the requirements a few times. If I am working on it a few days, I reread them each day. Then I reread them before I submit my code. It is so easy for small details to fall through the cracks or let your assumptions take over. Reread it, CLOSELY.

Make a function that looks through an array of objects (first argument) and returns an array of all objects that have matching name and value pairs (second argument).

Reread this: “matching name and value pairs”. You are checking if that name (key) matches anything anywhere and the same with the only value. But we only care about the pair. If source is { "apple": 1 }, then we don’t care if the collection has “apple” somewhere and/or it has “1” somewhere, we care if that pairing exists, if it has the k/v pair of "apple": 1 somewhere. That is what we care about. That is what we should be checking. I think you need to simplify your looking structure. When we’re looping for testing (not the outside loop), we should be testing combinations of k/v pairs. That would have a different structure.

Hint:

loop collection
  loop source k/v pairs
    loop collection object k/v pairs
      compare collection object k/v pair and source k/v pair

To me, that makes it clear. If I were to explain the algorithm to someone, I would say: We have to check each object in the collection and see if each k/v pair in the source exists in each of those collections.

To me, each of those are saying the same thing, the pseudo code and the text. If you need to, write the text first. Then write the pseudo code. Then write the code. But think it through, don’t just jump to the coding. There’s an old satirical saying in programming: Hours of debugging can save you minutes of planning.

The pseudo code and text are a little vague on the logic inside the loops, but I’ll leave that for you.

That’s not that far off from what you have but instead of trying every combination of k/v, we are just looking at the pairings that exist in our data.

I took a day break.
This is the progress I have made including I tried breaking it down step by step.

The code passes the first two functions, as I come down to 3rd I’ll put it inside my code.

The reason it fails because of this line (collection[i].hasOwnProperty(sKeys) it returns false, but why? sKeys is a property of collection[i]… waiting for your answer please :slight_smile:

function whatIsInAName(collection, source) {
  const arr = [];
  // Only change code below this line
  for (let i = 0; i < collection.length; i++) {
    const sKeys = Object.keys(source)
    for (let names in source) {
      for (let props in collection[i]) {
        if (collection[i].hasOwnProperty(sKeys) && source[props] == collection[i][props]) {
          arr.push(collection[i])
        }
      }
    }
  }
  // Only change code above this line
  return arr;
}

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

/*
loop through collection // get the props/values
if collection props/values match source props/value

push that object to arr.

*/

// how to match collection props/value to source prop/value
//sKeys can check if such property exists in collection[i]
// if (collection[i].hasOwnProperty(sKeys))
//now need to check if sKeys value matches to collection[i] value
//source[props] == collection[i][props]

OK, this is a big step in the right direction.

First, a mistake on my part. I was trying to do this from memory. You don’t want to loop over the collection object k/v pairs. The pseudo code should have been:

loop collection
  loop source k/v pairs
      compare collection object k/v pair and source k/v pair

So, you can remove that loop.
Also, get rid of this line:

const sKeys = Object.keys(source)

We don’t need that. The for loop on the line after is going to loop over those keys for us.

That just leaves the logic of when to push the data. Your logic:

        if (collection[i].hasOwnProperty(sKeys) && source[props] == collection[i][props]) {
          arr.push(collection[i])
        }

seems to be, if this collection object has the source key && the source of current prop === the collection object of that prop, then push.

Ignoring some issues with naming, I think that logic is wrong. We don’t push every time we have a match. What if a collection object has more than one match? I think we need to think in the opposite way. When we loop over the source data, if we find a source k/v pair that doesn’t match, then we don’t want to push. In other words, we won’t know we need to push until after we’ve checked all of source’s k/v pairs. We also don’t need to check if the key exists - if it doesn’t exist, accessing that key will just return undefine, which won’t match. This:

source[props] == collection[i][props]

is basically on the right track, but we should be using the source key and I think the logic needs to be inverted. And instead of pushing, we should be setting a flag (like a boolean variable) to tell us not to push.

So, the push is going to be in a different location and should be dependent on that flag.

Like I said, you’re really close now. You just need a little refactoring.

Sorry for mislead.

Here is a hint on the pseudo code I just used to actually solve this (as opposed to me trying to foolishly do this from memory.

Again, try to do it without checking this:

loop collection
  set flag for difference found to false
  loop source k/v pairs
    if collection object's value at this source key does not match source's value at this key
      set flag for difference found to true
      break out of loop // optional, no need to check others once diff found
  if no diff found
    push collection object onto answer

But again, you’re really close. You did a good job implementing the explicit parts of my pseudo code (even though I made a mistake.)

question is what is a match exactly?
For my understanding a match is when both collection objects property equals to a source object property, and same for values then it is a match.

I think I understand the issue, my program pushes it all the time but it needs to only push it once.

Right, a key (property) and value have to match. If the source has a key, then that collection object has to have that key with the same value that source has for that key. Another way to say it is that the set of k/v pairs has to be a subset of the k/v pairs in the collection object. It is the pairs that have to match, not just the keys or the values. If the source 1 apple and 3 bats, the that collection object has to have 1 apple and 3 bats. It can have other things but it at least needs exactly 1 apple and 3 bats. Another way to say it is that you could remove k/v pairs from the collection object and be left with the source.

Look at the tests and what the expected answer is to help you understand.

I haven’t opened your hints, but I don’t quite understand your logic.

I have to understand what’s going on my own so I followed the loop and came up with this.

I need to make sure that every element passes my test and then I can use an if statement and push, because I noticed that previously even if one of props matched it went through and I need everything.

edit: i fixed some of my every function syntax but still seem to have issues ;p

I think that’s what u meant by having a flag variable.

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

  for (let i = 0; i < collection.length; i++) {
    for (let names in source) {
      for (let props in collection[i]) {
        const test = collection.every((num) => {
          source.hasOwnProperty(num) && source[names] == collection[i][props]
        })
        console.log(test)

      }
    }
  }

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

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

/*
loop collection // get object
loop source object // get keys
loop collection[i] object // get keys
implement a test that every element of object is a property of source object and also equal to its value.
if (test) {
  push element to arr
}

*/

As I said, you don’t need the for (let props in collection[i]) { loop. You’ve also added a loop with your every method - that is really just a loop. Additionally, even if you needed, that every loop will not work because you are not using the iterated variable and your callback function is not returning anything so every will always return false. But there’s no need to fix that because you don’t need it.

So, you have this:

loop collection objects
  loop source props (names)
    loop collection object props (props)
       loop collection objects to check if every matches test

That is 4 loops. You only need the first two. We need to loop the individual collection objects, then loop the source k/v pairs (or just the keys/names to access the values, which is what you are doing here, perfectly fine). With that, you are going to check each source k/v pair and see if there is a corresponding k/v pair in the current collection object (from the outer loop).

So, let’s talk about this line:

source.hasOwnProperty(num) && source[names] == collection[i][props]

This is not right, but is salvageable. As I said before, you don’t need this:

source.hasOwnProperty(num) 

we don’t need to check if it anything has the property - if it doesn’t, then
when we try to access that property, it will just evaluate to undefined and won’t equal anything.

This:

source[names]

is basically right.

Don’t use ==, use ===. Always use the latter unless you specifically want type coercion.

And this:

collection[i][props]

Is props what we want there? Again, that loop should disappear anyway. But the point of the comparison should be checking if source for a given property has a corresponding value in the current collection object for that same property. Those last 4 words are very important.

Also, while we’re at it:

for (let names in source) {

Is “names” the best variable name? I used “sourceProperty” in my solution, “key” would makes sense too, as would “property” or “prop”. But I get that you are keeping it close to the problem description, which used that word. My issue is that it shouldn’t be plural. On each iteration of the loop, that will only contain one name, not more than one. To me, it’s a little confusing.

So, the last thing is the flag variable. Yeah, you kind of have the idea, but don’t use a loop here (which every is). You just need a simple test. you have a key (you’re currently calling it “names”). We know that it exists in source because that is where we got it. We want to see if the current collection object has a value for that key that matches the value that source has for that key. That is our test. That is what is meant in the instructions by “matching name and value pairs”. Does a given collection object have a k/v pair that matches every source k/v pair.

And like I said, I found that I had to invert the logic. It was easier for me to keep track if any were found that didn’t match.

Once you have an effective flag variable, you need to figure out where and how to use it. It won’t be right after the check, because you only want to push if they match for all the source keys.

For now, don’t use any array methods. I’m sure it can be done, but let’s solve it with two for loops first. If we can’t do that, then we won’t be able to use the method version because those are just disguised for loops. So, solve this the basic way, with for loops, then we can see if we can make this work with an every - which will just be based on what we wrote anyway.

1 Like

OK, I made a solution with just prototype methods. But it is the same logic as the simpler for loop method so you can’t understand one without the other. Get the version with two for loops and then we’ll see about changing it to the “sexier” prototype method version.

This is an important technique. When I was doing job interviews and would be given an algorithm to solve, I’d always solve it the easiest way first. Then I’d try to make it more “clever”. It’s better to have a working solution than to struggle and fail with a more clever one.

1 Like

so this is the condition check collection[i][sourceKeys] === source[sourceKeys]
I assume I don’t need to check if the collection object has that property since if the collection object has the same value of the sourceKey value then they share a property is that correct?

now about the flag variable I find it a bit confusing how to implement it, that’s why I wanted to use every method because it seems the same just easier in my head.

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

  for (let i = 0; i < collection.length; i++) {
    for (let sourceKeys in source) {
    if   (collection[i][sourceKeys] === source[sourceKeys]) {
    }
    }
  }

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

so this is the condition check collection[i][sourceKeys] === source[sourceKeys]

Yeah that’s basically it. I still think the variable name should be singular since it holds only one key/name.

I assume I don’t need to check if the collection object has that property since if the collection object has the same value of the sourceKey value then they share a property is that correct?

Right. We know source will have it. If the collction object doesn’t have it, it will just evaluate to undefined and won’t match - what we want.

now about the flag variable I find it a bit confusing how to implement it, that’s why I wanted to use every method because it seems the same just easier in my head

The inner loop could be replaced with that. Let’s do it with a for loop first. We can repace it later. But you should know how to do it with a for loop.

I’m not at a computer at the moment to test it, but the code you have looks right. Now we need to know when to push. We can’t do it in the if section because then it will push for every k/v pair that matches. We only want to do it when all the k/v pairs match, so we have to do the push after the inner loop. We need a variable in the if logic to let it know if they all matched.

As near as I can see, you have to add code in three places. You need to decalre and initialize your flag, you need to do something to it inside the if section, and then you need to use that flag to know whether or not to push.

You’re really close.

I don’t know how to implement this without the methods.

You can have a boolean variable called doAllMatch thqt starts out as true. If one does not match, you flip it to false (you’ll have to invert your logic, !== instead of ===) and then use that variable to tell if you need to push.

what I can’t understand is why { "bat": 2 }
this object is being pushed if I have (collection[i][sourceKey] === source[sourceKey]) in my code, I followed every loop and when they get to bat one of them is undefined so how come it allows it?

We couldn’t possibly guess without seeing your latest code.

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

  for (let i = 0; i < collection.length; i++) {
    console.log(collection[0])
    console.log(collection[1])
    console.log(collection[2])
    let doAllMatch = 0
    for (let sourceKey in source) {
    console.log(collection[1][sourceKey]) // collection obj value with sourceKey
    //source[sourceKey] value of source Obj key
     if (collection[i][sourceKey] === source[sourceKey]) {
       doAllMatch = true;
     }
    }
   if (doAllMatch === true) {
     arr.push(collection[i])
   }
  }
console.log(arr)
  // Only change code above this line
  return arr;
}

What does this mean, in words?

if collection object accessing sourcekey value equals to source object source key value
doAllMatch = true