Filtering an array against an entire other array

The $ scope.categories array is populated from a multi-select element in AngularJS.

$scope.categories = ["Adventure", "Strategy"]

      

I need to compare this array with the categories array in the items array below:

$scope.items = [
    {
        title: "Star Wars",
        categories: ["Adventure"]
    }, {
        title: "Star Search",
        categories: ["Adventure", "Strategy"]
    }, {
        title: "Star Trek",
        categories: ["Adventure", "Family"]
    }, {
    title: "Star Wars",
    categories: ["Family", "Strategy"]
}];

      

Both values ​​in $ scope.categories must match the same values ​​in $ scope.items.categories for the object to be moved to the output array.

This results in $ scope.filtered array (items 1 ):

{
  title: "Star Search",
  categories: ["Adventure", "Strategy"]
}

      

I have logic until the loop repeats ... but how?

  • I am browsing $ scope.categories
  • Then loop through $ scope.items
  • Then loop through the $ scope.item.categories array of each object.
  • Then I compare the value of $ scope.categories with the value of $ scope.item.categories

    for (var i = 0; i < categories.length; i++) {
      for (var j = 0; j < items.length; j++) {
        for (var k = 0; k < items[j].categories.length; k++) {
          if(categories[i] == items[j].categories[k]) {
            console.log("The value of " + categories[i] + ", matches " + items[j].categories[k]);
          } else {
            console.log("The value of " + categories[i] + ", is not a match");
          }
        }
      }
    }
    
          

Here is a JSbin example

+3


source to share


2 answers


It's much easier:

var filtered = items.filter(function(i) {
  return categories.every(function(c) {
    return i.categories.indexOf(c) >= 0
  })
})

      

Array.prototype.filter

iterates over the array and calls a callback function on each element. Each element that returns a true callback is included in the result array.



Array.prototype.every

iterates through the array and calls a callback for each element, returning true if the callback for ALL elements returns true, and false otherwise.

In this case, we are filtering the array of elements, and the filter callback returns true when all categories "pass" the callback condition every

, which is that the item categories contain the current ( c

).

(Here's a fixed JSBin )

+6


source


It's a little easier here. He assumes that ordering and capitalization are accurate. Interestingly, filter + join successfully outperforms filter + every method. If the top-level categories (i.e. var categories = ["Adventure", "Strategy"]

) are assumed to be a subset, then the function join()

can be combined with indexOf

. The solution will still be simpler, but the performance will still be slightly better.

See performance test here: http://jsfiddle.net/t3g3k7tL/ .

var categories = ["Adventure", "Strategy"];

var items = [
    {
        title: "Star Wars",
        categories: ["Adventure"]
    }, {
        title: "Star Search",
        categories: ["Adventure", "Strategy"]
    }, {
        title: "Star Trek",
        categories: ["Adventure", "Family"]
    }, {
        title: "Star Wars",
        categories: ["Family", "Strategy"]
    }
];

var filtered = items.filter(function(element) {
    return element.categories.join("") === categories.join("");  
});

      



If the top level categories are a subset and not an exact match, then the following solution will work:

var filtered = items.filter(function(element) {
    return element.categories.join("").indexOf(categories.join("")) !== -1;    
});

      

+1


source







All Articles