Been stuck on this one for a few days. So far, I’ve solved 3/4. Can’t seem to get the final one. Any guidance is much appreciated:
function whatIsInAName(collection, source) {
// What's in a name?
var arr = [];
// // Only change code below this line
var sourceKeys = Object.keys(source);
var h;
sourceKeys.filter(function(x) {
h = x;
return h;
});
collection.filter(function(e) {
if(e.hasOwnProperty(h)) {
arr.push(e);
}
});
// Only change code above this line
return arr;
}
whatIsInAName([{ first: "Romeo", last: "Montague" }, { first: "Mercutio", last: null }, { first: "Tybalt", last: "Capulet" }], { last: "Capulet" });
That’s not how you use filter (it’s kinda working accidentally atm). First filter:
sourceKeys.filter(function(x) {
h = x;
return h;
});
So sourceKeys is an array of keys (strings). A non-empty string is always coerced to true
, so the first filter goes through each key, and returns true for it, so you get exactly the same array of keys out the other end. You aren’t saving the result anywhere (filter creates a new array), so it might as well not be there. However you’re assigning to h
on each value. Once you’ve gone through that array, h
will end up being assigned the last object key present. So this is why it works for some things: if you have a single key - { last: "Capulet" }
, h
will be last
. If you have more than one, h
will be the last key - for { first: "Tybalt", last: "Capulet" }
, h
will probably be last
(can’t guarantee it, but probably).
Second filter:
collection.filter(function(e) {
if(e.hasOwnProperty(h)) {
arr.push(e);
}
});
So on this, you’re checking if each collection item has one property in the source (h
). Again, not using filter correctly (forEach
would be what you use for side effects).
If you’re keeping the arr = []; return arr;
bit, and push
ing to array, it maybe makes more sense to use forEach
or a loop, whereas if you’re using filter
, you can just return collection.filter(function dostuff() {})
.
- With
filter
, you normally would be using it to take collection
and filter out the items that don’t match - this gives you a new resultant array.
- With the loop approach, you are going through
collection
, picking the ones that do match and adding them to arr
.
If you mix the two approaches it gets a bit complicated, like here.
Using Object.keys
is fine, but you need to check every key in source
, at the minute you’re just checking one
Thanks for your reply @DanCouper.
After doing some more research, this is the code I’ve come up with:
function whatIsInAName(collection, source) {
// What's in a name?
var arr = [];
// Only change code below this line
var sourceKeys = Object.keys(source);
collection.forEach(function(key) {
Object.keys(key).forEach(function(k) {
sourceKeys.forEach(function(s) {
if(key.hasOwnProperty(s) && source[s] == key[k]) {
arr.push(key);
}
});
});
});
// Only change code above this line
return arr;
}
It works for the first 2/4 answers. If I understand correctly, we need to check if each element in collection
has the same key/value pair of source
. I think my code correctly checks the values and keys, but I’m having a hard time pushing the specific elements from collection
that match the key/value of source
. Does this sound like it’s more on the mark? (p.s. sorry for the late reply, I had to catch a flight and get settled. I appreciate your response from before).
With your code above consider this scenario:
whatIsInAName([{ "a": 1 }, { "a": 1, "b": 2, "c": 2 }, { "a": 1, "b": 2 }], { "a": 1, "b": 2 });
when the code reaches:
sourceKeys.forEach(function(s) {
if(key.hasOwnProperty(s) && source[s] == key[k]) {
arr.push(key);
}
for the first time:
s: a
key: { "a": 1 }
source: { "a": 1, "b": 2 }
Result: The if statement
evaluates as true
and the key
object is added to the arr
array, even though it should have never been added, since key: { "a": 1 }
is not a proper match of the source: { "a": 1, "b": 2 }
, since the key "b"
is missing from the key
object.
Even when:
key: { "a": 1, "b": 2 }
source: { "a": 1, "b": 2 }
You’re going to push the key
object to the arr
array two times, because the if statement
will evaluate to true for each of the keys, i.e., a and b
so the key
object gets pushed to arr
array for each of the keys.
Problem: You’re pushing the key
object to the arr
array for each key and value pair in the key
object that matches the corresponding key and value pair in the source
object.
What’s required to be done: You should push the key
object to the arr
array only when all the keys of the source
object are present in the key
object, and each of the keys have same corresponding values in both the objects.
1 Like
I agree with @pranav-kural’s approach. I had this same issue with duplicates and I was stuck to on this for a bit. I solved it by dissecting the last sentence of the problem – “Each property 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”.
My approach did exactly that: add the element from collection ONLY if it matched all of the source key: value pairs. That way each collection item is evaluated exactly ONCE and added if there is a of all source key: value pairs are present or excluded if not.
I found this MUCH easier than building an array and then to try and remove/filter from the existing collection if it failed one of the tests. This approach is more intuitive as a human, but seemed more complicated to code. In either case, the end result should be the same, but my approach eliminates the need for reduce/filter and at least for me reduces the logical complexity of the problem.
1 Like
Thank you @pranav-kural and @islandrob for your responses.
I’m still trying to wrap my head around how to code the approach you both suggest. I’m having trouble comparing the keys. I don’t get how to evaluate the keys in source only once when they are inside the loop.
function whatIsInAName(collection, source) {
// What's in a name?
var arr = [];
// Only change code below this line
var sourceKeys = Object.keys(source);
collection.forEach(function(key) {
if(key[sourceKeys] === source[sourceKeys] && Object.keys(key) === sourceKeys) {
arr.push(key);
}
});
// Only change code above this line
return arr;
}
@jawaka72 That’s completely fine. That’s what it is all about, problem solving.
Hint: how about a loop inside the loop 
collection.forEach(function(key) {
// key object represent one object from collection
// Now:
// FOR EACH key of SOURCE object
// Check if the 'key' object has that key AND the same value for that key
// if TRUE; do something
// if false; do something else
// finish this FOR EACH (or FOR)
// if all the keys of source found a match in the 'key' object and the values are same too
// i.e., each loop of SOURCE FOR EACH (or FOR) above evaluated to TRUE
// Then add the 'key' object to 'arr' array
// Now go for the next 'key' object
});
Take one step at a time. First make sure the 'key'
object is a proper match (have all key:value pair of source object). Then the second step is to add the 'key'
object to the 'arr'
object if it successfully passes the first step. Don’t combine both the steps.
This is just a very stripped down pseudocode of how this can be handled, you can mold it to your own way or choose to do something completely different, it’s upto you how you actually program it.
2 Likes
As an aside, you can either (1) start with not including the object and then include it if the object passes all tests or (2) assume you will include the object and then eliminate it if it fails any of the tests. I found the latter easier to code.
1 Like
@pranav-kural and @islandrob
Thank you both so much for your guidance and patience. It’s working now!
Solution:
function whatIsInAName(collection, source) {
// What's in a name?
var arr = [];
// Only change code below this line
var sourceKeys = Object.keys(source);
var hasKey;
collection.forEach(function(key) {
sourceKeys.forEach(function(s) {
if(key.hasOwnProperty(s) && key[s] === source[s]) {
hasKey = true;
} else {
hasKey = false;
}
});
if(hasKey === true) {
arr.push(key);
}
});
// Only change code above this line
return arr;
}
I think I was trying to do too much at once and wasn’t breaking down the problem enough. Couldn’t have done it without both of your support. Thanks again!
No prob. I do think your solution has an error that I have also seen in others.
Consider this case: whatIsInAName([{ “a”: 1, “b”: 2 }, { “a”: 1 }, { “a”: 1, “b”: 2, “c”: 2 }], { “a”: 100, “c”: 2 }) ;
Should return [].