2

Currently I am working on an JavaScript exercise.

The aim of the exercise is to compare the array which formed with different objects. there are 2 parameters for the function: The collection is the main array, and the source is the comparison criteria. Only the objects in main array which contains the key and same properties as source array would be returned.

The outcome should be as follows:

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 []

To tackle this, I use the array filter function and aim to select the object which matches the criteria and return as new array. The code is as follows:

function whatIsInAName(collection, source) {
  var arr = [];
  var key = Object.keys(source)

  return collection.filter(function(x){
    for(var y = 0; y < key.length; y++){
      if(x.hasOwnProperty(key[y]) && x[key[y]] == source[key[y]]){
        return true;
      }
    }
  return true;
  }) 

}

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

However, when I test the condition in the console, it returns the following:

 [{…}, {…}, {…}]

And at the same time, I saw that there is an similar answer. But the difference is, it use the negative selections which return false when there is sth not matched like this:

function whatIsInAName(collection, source) {
  var srcKeys = Object.keys(source);

  return collection.filter(function (obj) {
    for(var i = 0; i < srcKeys.length; i++) {
      if(!obj.hasOwnProperty(srcKeys[i]) || obj[srcKeys[i]] !== source[srcKeys[i]]) {
        return false;
      }
    }
    return true;
  });
}

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

This time it works.

So what I want to know is, what is my error on the first script? And if I still want to use the positive criteria i.e only return cases which match the both criteria, how can I make it? Many thanks

0

2 Answers 2

3

In your original function that is not working you are always returning true from the filter callback, so nothing gets filtered out.

But, you should also be able to just filter and use every with Object.keys of the search object:

function whatIsInAName(arr, search_obj) {
     return arr.filter(obj => Object.keys(search_obj)
           .every(key => obj[key] === search_obj[key]))
}


console.log(whatIsInAName([{ first: "Romeo", last: "Montague" }, { first: "Mercutio", last: null }, { first: "Tybalt", last: "Capulet" }], { last: "Capulet" }))
 // [{ first: "Tybalt", last: "Capulet" }].

console.log(whatIsInAName([{ "apple": 1 }, { "apple": 1 }, { "apple": 1, "bat": 2 }], { "apple": 1 }))
// [{ "apple": 1 }, { "apple": 1 }, { "apple": 1, "bat": 2 }]

console.log(whatIsInAName([{ "apple": 1, "bat": 2 }, { "bat": 2 }, { "apple": 1, "bat": 2, "cookie": 2 }], { "apple": 1, "bat": 2 }))
// [{ "apple": 1, "bat": 2 }, { "apple": 1, "bat": 2, "cookie": 2 }]

console.log(whatIsInAName([{ "apple": 1, "bat": 2 }, { "apple": 1 }, { "apple": 1, "bat": 2, "cookie": 2 }], { "apple": 1, "cookie": 2 }))
//  [{ "apple": 1, "bat": 2, "cookie": 2 }]

console.log(whatIsInAName([{ "apple": 1, "bat": 2 }, { "apple": 1 }, { "apple": 1, "bat": 2, "cookie": 2 }, { "bat":2 }], { "apple": 1, "bat": 2 }))
// [{ "apple": 1, "bat": 2 }, { "apple": 1, "bat": 2, "cookie":2 }]

console.log(whatIsInAName([{"a": 1, "b": 2, "c": 3}], {"a": 1, "b": 9999, "c": 3}))
// []

Sign up to request clarification or add additional context in comments.

1 Comment

Beat me to it by seconds, well done! BTW the obj[key] && is totally unecessary and it may cause problems if the property is 0, false, ... etc: whatIsInAName([{"a": 0}, {"a": 0, "b": 1}], {"a": 0})
1

Your original code doesn't work because you are returning true as soon as a property matches the criteria. It doesn't proceed with checking the rest of the keys.

For example imaging x is { a: 1, b: 2 } and source is { a: 1, b: 1}. Your original code checks the property a, it matches, so true is returned from filter. It doesn't proceed to check if b matches or not (because return terminates the function immediately).

The second code example does exactly the opposite, it stops checking the properties once it finds one that doesn't match (if a property doesn't match then checking the rest will be redundant).

Original code: searches for a property that matches the criteria. If one is found, true is returned, if none found, false is returned. This code fails only if all keys fail to match, succeeds if at least one key matches.

Second code: searches for a property that doesn't match. If one is found, false is returned, if none found, true is returned. This code fails if at least one key fails to match, succeeds if all keys match.

2 Comments

hi ibrahim, this is simply great and really helpful! And a question: As I know that filter is going to scan all elements in the array. Before I always understand that, in this case it will just scan through all the objects in the array, but the filter will scan through the key and the properties in every object indeed?
@PakHangLeung filter does scan through the whole array, but it's your function that should scan through the objects. filter loops over the array and call the function you privide to it on each object from the array, it then see if that function call yields true or false to decide whether the object should be included. filter doesn't know what the function does and doesn't care, it only cares about its return value. In your original code, the function you pass to filter doesn't do its job, it just check if at least one key (of the passed in object) matches to return true.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.