freeCodeCamp Challenge Guide: Wherefore art thou

Wherefore Art Thou


Problem Explanation

Write an algorithm that will take an array for the first argument and return an array with all the objects that matches all the properties and values in the Object passed as second parameter.

Relevant Links


Hints

Hint 1

You may use for loop or the filter() method.

Hint 2

Consider using the hasOwnProperty() method to know if the property name exists in an object (as its own property).

Hint 3

Check if the value of the property in a collection objectmatches the values associated with the keys of thesource` object.


Solutions

Solution 1 (Click to Show/Hide)
function whatIsInAName(collection, source) {
  // What's in a name?
  const collectionMatches = [];

  for (let i = 0; i < collection.length; i++) {
    let foundMismatch = false;

    for (const sourceProp in source) {
      if (collection[i][sourceProp] !== source[sourceProp]) {
        foundMismatch = true;
      }
    }
    if (!foundMismatch) {
      collectionMatches.push(collection[i]);
    }
  }
  return collectionMatches;
}

Code Explanation

  • We use a for loop to iterate over every object in the collection.
  • We check for a mismatching value between the sources keys and the current object.
  • If no mismatch is found, the current object is added to the array of collectionMatches.
Solution 2 (Click to Show/Hide)
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;
  });
}

Code Explanation

  • We filter through the array using .filter().
  • Using a forloop we iterate through each item in the object.
  • We use a if statement to check if the value of the current property for the object matches the value in source.
  • We return false if any mismatch is found. Otherwise, we return true;
Solution 3 (Click to Show/Hide)
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 sourceKeys = Object.keys(source);

  return collection
    .filter(obj => sourceKeys
      .every(key => obj[key] === source[key]));
}

Code Explanation

  • We filter through the collection using .filter().
  • We return a Boolean value for the .filter() method by checkif if .every() source key value matches the current object’s value.
112 Likes

There is a bug in the ‘advanced’ solution above. I made a PR to the old wiki project, but was told to contribute here instead. Would be great if the ‘advanced’ solution could be updated. Details can be found here: https://github.com/FreeCodeCamp/wiki/pull/1246

10 Likes

@sautille is still a ‘new user’, once enough trust/reputation is built to progress to ‘basic user’ people can edit wiki posts.

From memory you just need to read a few posts and interact a little to ‘level up’.

Edit

4 Likes

This is just a basic mechanism to avoid spaming.

3 Likes

@sautille: I have updated the changes to match and reflect the pull request you made earlier and the change on the main repository as well.

Thanks for contributing.

7 Likes

Great post i apprecialte your efforts!

2 Likes

I’ve tried to solve this and gotten 2/4 green marks but gave up and came here to take a peek.

But I only got more confused. The exersize says to only change code between the lines.

 var arr = [];
  // Only change code below this line
  
  
  // Only change code above this line
  return arr;

But all the solutions posted here ignore that.

34 Likes

I also got 2/4 green marks using the code below… I understand why my code doesn’t work but I was wondering if anyone knows if it’s possible to make this approach work (without using the filter() method) considering that the exercise tells us not to change the code between the lines. Cheers!

function whatIsInAName(collection, source) {
  // What's in a name?
  var arr = [];
  // Only change code below this line
  for (var i = 0; i < collection.length; i++) {
    for (var prop in source) {
      if (collection[i].hasOwnProperty(prop) && collection[i][prop] === source[prop]) {
        arr.push(collection[i]);
      }
    }
  }
  // Only change code above this line
  return arr;
}

whatIsInAName([{ "a": 1, "b": 2 }, { "a": 1 }, { "a": 1, "b": 2, "c": 2 }], { "a": 1, "b": 2 });
3 Likes

@jacobworrel I think your code is pushing to arr when only ONE property is matched. However, sometimes the source has more than one, an all must be matched.

5 Likes

Right.I just wonder if there’s any way to make my code work when there is more than one property…

4 Likes

Help! Can anyone tell me why my code removed the "" from the keys in my solution?
Here’s my code:

function whatIsInAName(collection, source) {
	var arr=[];
	var k = Object.getOwnPropertyNames(source); 
	var v = source[k]; // assigns var to value in key value pair

	//loop through array of objects
	 for (var i = 0; i < collection.length; i++) {

		//check each object for matching key
	 	if (collection[i].hasOwnProperty(k)) {
	 		
			if (collection[i][k] == v ) { // if object has a match of key value pair...
				arr.push(collection[i]); // add object to arr
			}
		}
	 }
	return arr;
}

The code passed for the 1st test:

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

but the 2nd didnt pass (keys aren’t in double quotes).

2nd test:
whatIsInAName([{ "a": 1 }, { "a": 1 }, { "a": 1, "b": 2 }], { "a": 1 })

my function returned this:

[ { a: 1 }, { a: 1 }, { a: 1, b: 2 } ]

instead of this:
[{ "a": 1 }, { "a": 1 }, { "a": 1, "b": 2 }]

Why were my keys changed when I pushed them to arr?

And why do none of the spoiler solutions use the starter code that was there? (where the last line is return arr?)

5 Likes

Is it the answer they were looking for? Probably not, given the replies, but I found a solution that works and just thought I would pass it on. Cheers!

function whatIsInAName(collection, source) {
  // What's in a name?
  var arr = [];
  // Only change code below this line
  
var name,
    key,
    soVal,
    coVal;
for(name in source){
  soVal = source[name];
} 
    var i=0;
      for(i; i< collection.length; i++) {
        var obj = collection[i];
          for(key in obj){
            coVal = obj[key];
            if (coVal == soVal && name == key) {
            arr.push(obj);
            }
          } 
      }
     
  // Only change code above this line
  return arr;
}
13 Likes

FYI, the code above for the advanced solution is corrected, but the repl.it it links to still has the old code.

do we have to use the methods in helpful links?

the filter() method works as well

60 Likes

This solution worked for me and it shares your approach. It passed all the test cases.

function whatIsInAName(collection, source) {
  var arr = [];
for (var keyd in source){
 }for (var key in collection){
     if (source[keyd] === collection[key][keyd]){
          arr.push(collection[key]);}
}return(arr);}
12 Likes

I solved this with a comparing the objects.
Here is the code:

function whatIsInAName(collection, source) {
  // What's in a name?  
  return collection
    .filter((obj) => JSON.stringify(Object.assign(JSON.parse(JSON.stringify(obj)), source)) === JSON.stringify(obj));
}

Does comparing the objects works faster or slower relatively to using the loop with comparing properties. (Sry for bad English :slight_smile: )

11 Likes

I solved that with couple for loops, it is not elegant as some above solutions, but it works:

function whatIsInAName(collection, source) {
  // What's in a name?
  var arr = [];
  
  arr = Array.prototype.slice.call(arguments);
  // Only change code below this line
  
  
  var key = Object.keys(source);
  var returncollection =[];
 
  for (i=0; i<collection.length; i++)
    {     
   for (j=0; j<key.length; j++)
    {
     if (collection[i].length<key.length)
       {
        break; 
       } 
     if (collection[i][key[j]]!==source[key[j]])
       {
        break; 
       }
      if (j==key.length-1)
       {
        returncollection = returncollection.concat(collection[i]);
       }
     }      
    }
    //collection2 = collection2.concat(collection[i]);     
  
  // Only change code above this line Object.keys(collection[i])[j]
  return returncollection;
}
8 Likes

Some really interesting solutions in here.

Here is my solution that turned out pretty close to the basic spoiler. I even ended up using some same variable names by coincidence.

I couldn’t figure out an elegant solution in the if/else statement like the basic solution spoiler does on this line:
if(!obj.hasOwnProperty(srcKeys[i]) || obj[srcKeys[i]] !== source[srcKeys[i]])
but I figured out an interesting way to get my return to be true if the tests were passed.

function whatIsInAName(collection, source) {
  // What's in a name?
  var arr = [];
  // Only change code below this line  

  arr = collection.filter(function(obj) {
    var srcKeys = Object.keys(source); 
    var test = 0;
    for (var i = 0; i < srcKeys.length; i++) {
      if (obj.hasOwnProperty(srcKeys[i]) && obj[srcKeys[i]] === source[srcKeys[i]]) {
        test += 1;
      } else {
        test += -1;
      }
    }
    return test === srcKeys.length;
  });
  
  // Only change code above this line
  return arr;
}

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