根据另一个数组过滤数组

4

$scope.categories数组是从AngularJS的多选元素中填充的。

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

我需要将此数组与下面项目中的类别数组进行比较:

$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"]
}];

为了将对象推入输出数组,$scope.categories中的两个值需要与$scope.items.categories中的相同值匹配。

结果为$scope.filtered数组(items1):

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

我已经掌握了循环的逻辑...但如何进行迭代?
  1. I am looping through $scope.categories
  2. Then looping through $scope.items
  3. Then looping through $scope.item.categories array of each object.
  4. Then I am comparing the $scope.categories value against 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");
          }
        }
      }
    }
    

这里是一个JSbin例子


@Mousey,我不知道还有其他的标签...谢谢。 - John Spiteri
2
也许可以为每个数组创建一个哈希值,然后比较这些哈希值?(@Amit的答案更好。) - ray
@Kutyel,我正在AngularJS筛选器中使用它,但试图简化和分离问题...这样做好还是不好? - John Spiteri
1
@JohnSpiteri 我也认为保持简单总是好的,但在stackoverflow上,只有与JavaScript相关的问题太过普遍了。在我看来,一个AngularJS标签会吸引更多的关注,而且如果有人不了解Angular,可能会对$scope等内容感到困惑...这只是个人意见 ;) - Kutyel
@Kutyel,我同意你的观点 - 我会更新我的方法。谢谢。 - John Spiteri
2个回答

7

实际上,它更简单:

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

Array.prototype.filter 遍历数组,并对每个项目调用回调函数。每个回调函数返回为true的项目都将包含在结果数组中。

Array.prototype.every 遍历数组,并对每个项目调用回调函数。如果所有项目的回调都返回true,则返回true,否则返回false。

在这种情况下,我们正在过滤项目数组,并且当所有类别“通过”every回调条件时,过滤回调返回true,即项目类别包含当前(c)类别。

(这是修复后的JSBin


能否请 @Amit 解释一下步骤?天才都是简单的。 - John Spiteri
1
@tymeJV 我使用这些标识符,因为这是问题中引用它们的方式,以及在 JSBin 中的方式。请不要“纠正”本来就没有错误的东西。 - Amit
如此简短、恰当和流畅。谢谢@Amit。 - John Spiteri
@Amit 非常感谢您的回答!我在react中尝试实现类似的功能,这个函数几乎就是我需要使用的。而且非常优雅。 - benmneb

1
这是一个更简单的解决方案。它假设排序和大写字母的使用是精确的。有趣的是,filter + join 解决方案的性能优于 filter + every 方法。如果假定顶级类别(即 var categories = ["Adventure", "Strategy"])是子集,则可以将 join() 函数与 indexOf 结合使用。解决方案仍然更简单,性能仍然略有改善。
在这里查看性能测试: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("");  
});

如果顶级类别是子集而不是完全匹配,则以下解决方案可行:
var filtered = items.filter(function(element) {
    return element.categories.join("").indexOf(categories.join("")) !== -1;    
});

我欣赏你的方法也行,等我有机会就会对答案进行比较 - 显然,效率更高的答案是最好的。谢谢。 - John Spiteri

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接