Intermediate Algorithm Scripting: Wherefore art thou - Help Needed With Comparing Object key-value pairs

Why does it return [undefined, undefined, undefined] right now? What am I doing wrong?

How do I compare the key-value pairs in two objects? I’m probably not understanding that.

Your code so far


function whatIsInAName(collection, source) {
var arr = [];
// Only change code below this line
arr = collection.map(elem => {
  if (Object.entries(source).includes(elem)) {
    arr.push(elem);
  }
})

// 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/84.0.4147.89 Safari/537.36 Edg/84.0.522.40.

Challenge: Wherefore art thou

Link to the challenge:

I think you’re using using Array.map() when you want something else. A .map() will replace every value in an array with the value that is returned by the mapping function. Your function doesn’t return a value, so it is replacing every value with undefined.

I changed it to:

function whatIsInAName(collection, source) {
  var arr = [];
  // Only change code below this line
  const sourceArr = Object.entries(source);
  for (const elem of collection) {
    if (sourceArr.includes(elem)) {
      arr.push(elem);
    }
  }
  
  // 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" }));

But it still doesn’t work; now I just get an empty array. What am I still doing wrong here?

If I walk through this, assuming test inputs like so:

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

So

collection = [
  { first: "Romeo", last: "Montague" },
  { first: "Mercutio", last: null },
  { first: "Tybalt", last: "Capulet" },
];
source = { last: "Capulet" };

So sourceArr is an array with one element. That element is an array with two elements, both strings, “last” and “Capulet”.

sourceArr = [["last", "Capulet"]];

So loop through collection.

Does sourceArr include a reference to the 1st object? No, only contains that array.
Does sourceArr include a reference to the 2nd object? No, only contains that array.
Does sourceArr include a reference to the 3rd object? No, only contains that array.

So arr is still an empty array, return that.


This is a perfectly good start you have here, with a good setup, but you’re missing a step. For each of those objects in collection, you’re going to want to loop over sourceArr and check if the value associated with each key is in elem (you could check if the key is there as well, but that check would be redundant: if the value for that specific key is there, then ipso facto the key has to be there). Only then can you be sure that you’ve got a match, and can push to arr.

includes is the wrong array method here, it can only test if a specific primitive value (or a specific object reference) exists in an array. This method may be helpful (or you can just loop over sourceArr on each for elem of collection loop iteration):

I tried this but I still get an empty array:

function whatIsInAName(collection, source) {
  var arr = [];
  // Only change code below this line
  collection.every(elem => {
    if (Object.keys(source) === elem.first
      && Object.values(source) === elem.last) {
        arr.push(elem);
      }
  })

  // 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" }));

What am I still missing?

You’ve removed your loop over collections you needed and the sourceArr variable you created, and now you’re using every on collection instead of the (now nonexistant) sourceArr. What you had was fine, you just needed to replace the include in the if condition.

Logic should be like:

setup empty array (arr)
convert source to an array of entries (sourceArr)

for each element in collection:
  if every key/value pair in sourceArr exists in element:
    push element to arr

return arr

I did that because just replacing includes with every in if condition like this:

function whatIsInAName(collection, source) {
  var arr = [];
  // Only change code below this line
  const sourceArr = Object.entries(source);
  for (const elem of collection) {
    if (sourceArr.every(elem)) {
      arr.push(elem);
    }
  }

  // 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" }));

gave me this error:

TypeError: #<Object> is not a function

in the console. So then there’s something I’m still not getting here.

Edit: Never mind. I need to write a good callback. May need help on that too but I’ll try something.

You need to define the callback function in every. So if you’re going over sourceArr, the callback takes the current item as an argument, so it ends up like:

function (sourceArrEntry) {
  // sourceArrEntry is like ["last", "capulet"]
  // check if that pair is in elem, return true if so, false if not
}

I tried this:

function whatIsInAName(collection, source) {
  var arr = [];
  // Only change code below this line
  const sourceArr = Object.entries(source);
  for (const elem of collection) {
    if (sourceArr.every(elem2 => {
      return elem.last === elem2.last && elem.first === elem2.first;
    })) {
      arr.push(elem);
    }
  }

  // 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" }));

I still get an empty array, though. So what’s wrong here?

SO:

  1. Remember what sourceArr is: it is an array of arrays, like [["last", "Capulet"]]. Elements in that array don’t have a first property or last property. For that example, the first and only entry is an array: entry[0] is “last”, and is the key. entry[1] is “Capulet”, and is the value.
  2. You should be checking if elem[key] matches the value for each of the entries in sourceArr

every says: “for every value in the array you give me, does the callback function return true”. If that is correct, it returns true, if not, then false

Okay, this is still isn’t working so I’m still doing something wrong here:

function whatIsInAName(collection, source) {
  var arr = [];
  // Only change code below this line
  const sourceArr = Object.entries(source);
  for (const elem of collection) {
    if (sourceArr.every(elem2 => {
      return elem.first === elem2[0] && elem.last === elem2[1];
    })) {
      arr.push(elem);
    }
  }

  // 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" }));

What am I doing wrong now?

Do you understand how the array functions (map, filter, every, etc) work, as in they run a function on every element in the array, and that function takes the element as the first argument?

Also that what Object.entries returns is an array of key/value pairs.

Also, you’re explicitly writing to the tests – you can’t hardcode first and last in here, because you have no idea in advance what the keys are you are looking for.

It may be easier for you to grok if I rewrite this as loop (note this is exactly the same logic as using every):

function whatIsInAName(collection, source) {
  var arr = [];
  // Only change code below this line
  const sourceArr = Object.entries(source);
  for (const elem of collection) {
    for (const keyValuePair of sourceArr) {
      if (elem.first === keyValuePair[0] && elem.last === keyValuePair[1]) {
        arr.push(elem);
      }
    }
  }

  return arr;
}

So

collection = [
  { first: "Romeo", last: "Montague" },
  { first: "Mercutio", last: null },
  { first: "Tybalt", last: "Capulet" },
];
source = { last: "Capulet" };

So sourceArr is an array with one element. That element is an array with two elements, both strings, “last” and “Capulet”.

sourceArr = [["last", "Capulet"]];

So loop through collection .

  1. Object is { first: "Romeo", last: "Montague" }. Is the value of first “last” and the value of last “Capulet”? No, don’t push.
  2. Object is { first: "Mercutio", last: null }. Is the value of first “last” and the value of last “Capulet”? No, don’t push.
  3. Object is { first: "Tybalt", last: "Capulet" }. Is the value of first “last” and the value of last “Capulet”? No, don’t push.

So arr is still an empty array, return that.

With this:

function whatIsInAName(collection, source) {
  var arr = [];
  // Only change code below this line
  const sourceArr = Object.entries(source);
  for (const elem of collection) {
    if (sourceArr.every(elem2 => {
      return elem.last === elem2[1] || elem.first === elem2[0];
    })) {
      arr.push(elem);
    }
  }

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

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

I’m passing the first and last of the tests, but failing all of the others. What am I missing for passing all of the tests?

You are hardcoding, and only checking first and last. You are assuming that every object is like { first: "Something", last: "Something" }. This is not the case. You are supposed to be checking if every key and value in sourceArr is present in the object that’s currently being checked – it doesn’t matter what those keys are

And again, sourceArr is an array of key/value pairs.

Object.entries({last: "Capulet" })

Gives you

[["last", "Capulet"]]

elem2[1] in this case is “last”. It’s the key of an object property. It’s never going to match the value of an object property. When you write elem.first === elem2[0], you’re saying “the value of elem.first is equal to the name of the key”

Doing return elem2 === elem; in the callback for every only makes the last test pass.

To check if a key is in an object:

key in obj
//or
obj.hasOwnProperty(key)

not this, which is what you’re doing

obj[key] === "name of key"

To check if the value of a key is in an object:

obj[key] === value

You have an array of [key, value] pairs. You want to check if each key is in the object you’re currently checking, and if each of the values in the object matches the value in that key value pair.

How do I do key in obj with elem and an element from array of key-value pairs that I have from the source object converted to an array? Object.keys(elem) or Object.values(elem) isn’t helping.

This is what I have:

function whatIsInAName(collection, source) {
  var arr = [];
  // Only change code below this line
  const sourceArr = Object.entries(source);
  for (const elem of collection) {
    if (sourceArr.every(sourceElem => {
      if () {
        return true;
      }
      return false;
    })) {
      arr.push(elem);
    }
  }

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

And what I want in the if condition is something that says “if key is in object”.

So you have an array of elements. Each element has two entries: the first one is a key, the second one is a value

[
  [key, value],
  [key, value],
  [key, value],
]

You are going through each of those one by one:

sourceArr.every(sourceElem => {

sourceElem is an array: [key, value].

sourceElem[0] is the key
sourceElem[1] is the value


Inside the loop, you have an object (elem).

Knowing that you can check if it has a key by writing SOME_KEY in elem, what value do you have available to you [one that represents the key of an object] that you could use instead of SOME_KEY?

And knowing you can check if a property value is the one you want by writing `elem[SOME_KEY] === SOME_VALUE, what do you have available to you that represents a. the key of an object, and b. the value of an object?

1 Like

I was able to do it like this:

function whatIsInAName(collection, source) {
  let arr = [];
  const sourceKeysArr = Object.keys(source);

  arr = collections.filter(elem => {
    for (const key of sourceKeysArr) {
      if (!elem.hasOwnProperty(key) || elem[key] !== source[key]) {
        return false;
      }
    }
    return true;
  });

  return arr;
}

Something like that.