Search code examples
javascriptarraysarray-filter

Error in applying array filter in array with multiple objects


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


Solution

  • 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.