Let's solve 'Wherefore art thou' another Intermediate JavaScript Algorithm Scripting Challenge, from freeCodeCamp. Here is what we are working with:
STARTER CODE
function whatIsInAName(collection, source) {
var arr = [];
// Only change code below this line
// Only change code above this line
return arr;
}
whatIsInAName([{ first: "Romeo", last: "Montague" }, { first: "Mercutio", last: null }, { first: "Tybalt", last: "Capulet" }], { last: "Capulet" });
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). Each name and value pair of the source object has to be present in the object from the collection if it is to be included in the returned array.
For example, if the first argument is [{ first: "Romeo", last: "Montague" }, { first: "Mercutio", last: null }, { first: "Tybalt", last: "Capulet" }]
, and the second argument is { last: "Capulet" }
, then you must return the third object from the array (the first argument), because it contains the name and its value, that was passed on as the second argument.
TESTS
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 }, { "bat": 2 }, { "apple": 1, "bat": 2, "cookie": 2 }], { "apple": 1, "bat": 2 }) should return [{ "apple": 1, "bat": 2 }, { "apple": 1, "bat": 2, "cookie": 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 []
Our Thoughts & Approach
The most important part of solving algorithm challenges is reading. Reading the starter code, the directions, and the tests thoroughly is the approach I take before jumping into coding. If the objective is not understood, I believe it will be a lot harder to code a solution. So what we are working with it is:
Our two inputs are an array of objects (
collection
) and an object (source
).We want our ouput to be an array.
We need to check if the second argument exists in the first argument (check if the key(s) exists).
Since we are working with objects and arrays in this challenge, let us see if there are any built-in methods we can use to help solve this.
After reading all the information, my first action would be to use a method, Object.keys()
, on the second argument, source
, to create an array from the keys of the object. I feel it would be easier to check against collection
.
let source = { "apple": 1, "bat": 2 };
console.log(Object.keys(source));
// Array [ "apple", "bat" ]
So we now have an array of objects and an array (of keys to check). The natural instinct (for me) is to now loop through collection
, but we want to check if the keys exist and then also check the values.
An array method we've used in the past to check if a test (that we set up) passes is Array.filter()
. It will create a new array of the items which pass the test.
One possible issue I can see is the fact we are looping through an array but we need to access an object within the array, not just primitive data types (string, number, etc.).
Reading up more about objects, there is a method for which you can check if the property exists on the object. Example below:
let dog = {name: 'Rusty', age: 4};
console.log(dog.hasOwnProperty('name'));
// true
Object.hasOwnProperty() on MDN
So, in short, we're going to use arr
, an empty array already declared, set that to collection.filter()
, loop through collection
, use hasOwnProperty()
to see if the key exists in the objects within collection
, then check if the values are equal, returning the items which are two. Let's break that down a little further:
var arr = []; // This code was provided
var sourceKeys = Object.keys(source) // Creates an array of keys
arr = collection.filter(function(obj) {
// obj is each item (an object) in collection
for (var i = 0; i < sourceKeys.length; i++) {
// we want to loop through based on how many keys they're were in source
if (!obj.hasOwnProperty(sourceKeys[i]) || obj[sourceKeys[i]] !== source[sourceKeys[i]])
// if obj doesnt have the key OR if obj key's value doesn't equal source key's value
// return false so it get's filtered out
{
return false;
}
}
return true;
// if it passed the above if statement, doesn't get filtered out and goes into arr
})
return arr;
// return an array
I didn't use pseudocode this time but I hope the comments were helpful.
Our Solution
function whatIsInAName(collection, source) {
var arr = [];
// Only change code below this line
var sourceKeys = Object.keys(source);
arr = collection.filter(function(obj) {
for (var i = 0; i < sourceKeys.length; i++) {
if (!obj.hasOwnProperty(sourceKeys[i]) || obj[sourceKeys[i]] !== source[sourceKeys[i]]) {
return false;
}
}
return true;
})
// Only change code above this line
return arr;
}
Resources & Links
'Wherefore art thou' challenge on fCC
Thank you for reading!
Top comments (6)
Another way:
Hey, quick question .. (bear with me as i'm still learning) ...
I have console.logged this line so many times and it always returns false ...
obj.hasOwnProperty(sourceKeys[i]), and since you have a ! in front of it, it would make it true. It's console logging an array which and not the actual keys in the array. Wouldn't the "obj" need to have a obj[0] in front of it for the hasOwnProperty() method to work ?!
And my second question is, arr = collection.filter(function(obj) .. how does javascript know that the obj value is the array collection ?!
Everything else i understand and thankful for your help!
I have the same doubts. Did you figure it out if you did let me know.
Great solution, but I did'not understood, why is the "return true" outside of the for loop. Then after each iteration where are the results of it is stored when the if is true.
function whatIsInAName(collection, source) {
// "What's in a name? that which we call a rose
// By any other name would smell as sweet.”
// -- by William Shakespeare, Romeo and Juliet
const souceKeys = Object.keys(source);
// filter the collection
return collection.filter(obj => {
for (let i = 0; i < sourceKeys.length; i++) {
if (obj[sourceKeys[i]] !== source[sourceKeys[i]]) {
return false;
}
}
return true;
});
}
whatIsInAName([{ first: "Romeo", last: "Montague" }, { first: "Mercutio", last: null }, { first: "Tybalt", last: "Capulet" }], { last: "Capulet" });
collection is an array of objects
source is just a single object .. it can contain more than 1 keys
now we stored keys of the source object inside the sourceKeys array
sourceKeys is an array that stores the keys from source
we are returning a filtered array
we are filtering the collection array
we are only using 1 parameter of the callback function for the filter method
this function will be called for each object that resides in collection array
we have the hold of keys from source object
we have the hold of a single object from collection array
now we are going to perform a for loop ..
why for loop ?
what are we looping ?
we are looping through keys ...
and we get hold of the single key from the source key
lets say that key is "last"
now with an if statement we are checking the values of obj[last] !== source[last]
so here comes your question why is return true out of forLoop
well the for LOop is designed to only return false no matter what ... so even if all the key's values matches the for loop will only care about false values
and its job is to see if there is something that is not matching
but since we are using a filter method that actually just takes true values and rejects false values to be incluced
in the end if nothing matches nothing will be added to array
but if it matches the whole object is said to be true and that object will be included
Thanks for this post! It helped me understand a lot better of the solution on FCC!