JavaScript: filter array of objects using an object

Viewed 71

I was trying to do the following challenge from freecodecamp: https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/wherefore-art-thou and I have a couple questions about it.

  1. Why is my attempt working in my local console but not on freecodecamp? Meaning, out of all the tests, 3 out of 4 are correct in my console, but none of them is on FCC.
  2. Why is this test whatIsInAName([{ "apple": 1, "bat": 2 }, { "bat": 2 }, { "apple": 1, "bat": 2, "cookie": 2 }], { "apple": 1, "bat": 2 }) not passing if all the others are?

My attempt with expected results:

function whatIsInAName(collection, source) {
  const arr = [];
  // Only change code below this line
  let finalObj = collection
    .map(item => Object.entries(item))
    .filter(el => String(el).includes(String(Object.values(source))))
    .map(el => Object.fromEntries(el))
  arr.push(finalObj);
  console.log(arr);
  // Only change code above this line
  return arr;
}
    
whatIsInAName([{ first: "Romeo", last: "Montague" }, { first: "Mercutio", last: null }, { first: "Tybalt", last: "Capulet" }], { last: "Capulet" }) // should return [{ first: "Tybalt", last: "Capulet" }]
whatIsInAName([{ "apple": 1 }, { "apple": 1 }, { "apple": 1, "bat": 2 }], { "apple": 1 }) // should return [{ "apple": 1 }, { "apple": 1 }, { "apple": 1, "bat": 2 }]
whatIsInAName([{ "apple": 1, "bat": 2 }, { "apple": 1 }, { "apple": 1, "bat": 2, "cookie": 2 }], { "apple": 1, "cookie": 2 }) // should return [{ "apple": 1, "bat": 2, "cookie": 2 }]
whatIsInAName([{ "apple": 1, "bat": 2 }, { "apple": 1 }, { "apple": 1, "bat": 2, "cookie": 2 }, { "bat":2 }], { "apple": 1, "bat": 2 }) // should return [{ "apple": 1, "bat": 2 }, { "apple": 1, "bat": 2, "cookie":2 }]
whatIsInAName([{"a": 1, "b": 2, "c": 3}], {"a": 1, "b": 9999, "c": 3}) // should return []

1 Answers
  • Using Object#entries, get the list of key-value pairs from source
  • Using Array#filter, iterate over collection. In every iteration, using Array#every, check if all entries in the above sourceEntries match the current object

function whatIsInAName(collection, source) {
  const sourceEntries = Object.entries(source);
  return collection.filter(e =>
    sourceEntries.every(([key, value]) => e[key] === value)
  );
}

console.log(
  whatIsInAName([{ first: "Romeo", last: "Montague" }, { first: "Mercutio", last: null }, { first: "Tybalt", last: "Capulet" }], { last: "Capulet" })
);
console.log(
  whatIsInAName([{ "apple": 1 }, { "apple": 1 }, { "apple": 1, "bat": 2 }], { "apple": 1 })
);
console.log(
  whatIsInAName([{ "apple": 1, "bat": 2 }, { "bat": 2 }, { "apple": 1, "bat": 2, "cookie": 2 }], { "apple": 1, "bat": 2 }) 
);
console.log(
  whatIsInAName([{ "a": 1, "b": 2, "c": 3 }], { "a": 1, "b": 9999, "c": 3 })
);

To analyze your code, let's add logs first:

function whatIsInAName(collection, source) {
  const arr = [];
  let finalObj = collection
    .map(item => { 
      const entries = Object.entries(item);
      console.log(1, entries);
      return entries;
    })
    .filter(el => {
      console.log(2, String(el), String(Object.values(source)));
      return String(el).includes(String(Object.values(source)))
    })
    .map(el => {
      const obj = Object.fromEntries(el);
      console.log(3, obj);
      return obj;
    });
  console.log(4, finalObj);
  arr.push(finalObj);
  console.log(5, arr);
  return arr;
}
    
whatIsInAName([{ "apple": 1, "bat": 2 }], { "apple": 1, "bat": 2 });

Your approach, in summary, is to iterate over the collection, convert each item to a list of entries. Then, filter those entries by checking if an item's entries (stringified) would include the values of the source (stringified). After that, convert back to objects the entries that passed the filtering resulting in an array of objects. Add this array to an initial array and return the latter.

Problems:

  • When source has more than one property it stops working, for example, you would be checking if "apple,1,bat,2" includes "1,2". Even if you check for source entries here you'll still be taking the assumption of the order of properties. In summary, stringifying the objects isn't the correct way, this is why it's better to check if every key in source match its value in a given item.
  • Third Array#map returns an array which you're then adding it (finalObj) to another array arr.
Related