【问题标题】:Strict Filter Array of Arrays数组的严格过滤器数组
【发布时间】:2015-11-17 02:10:58
【问题描述】:

我有一个标签输入框,similar to this,但我需要将其限制为允许的组合。

以下是可能的组合示例:

[{
      "Combo": [
        {
          "Id": 1,
          "Name": "Tag1"
        },
        {
          "Id": 3,
          "Name": "Tag3"
        }
      ]
    },
    {
      "Combo": [
        {
          "Id": 2,
          "Name": "Tag2"
        },
        {
          "Id": 3,
          "Name": "Tag3"
        },
        {
          "Id": 4,
          "Name": "Tag4"
        }
      ]
    }]

我首先获取一个不同的标签列表并将它们显示给用户。选择标签后,我需要通过传递的组合过滤标签。因此,如果我选择 Tag3,我应该得到 Tag1、Tag2 和 Tag4 的可用标签。我能够通过遍历数组数组并通过 id 数组获取组合的索引来完成此操作。像这样:

ids.indexOf(combos[a].Combo[c].Id) !== -1

但是问题是当我将 Tag2 添加到 id 数组时,indexOf 仍然包含第一个组合,因为 Id:3。我想要的是找到具有匹配或更多 Id 的组合。

所以当我通过这个时:

var ids = [3, 2];

我想要这个组合:

[{
      "Combo": [
        {
          "Id": 2,
          "Name": "Tag2"
        },
        {
          "Id": 3,
          "Name": "Tag3"
        },
        {
          "Id": 4,
          "Name": "Tag4"
        }
      ]
    }]

这有点乱,但这是我一直在研究的 jsfiddle 示例。 http://jsfiddle.net/4L3kr052/

【问题讨论】:

  • 您能否详细说明“我需要通过传递的组合过滤标签。”?

标签: javascript angularjs indexof angularjs-filter


【解决方案1】:

我创建了一个小提琴来解决这个问题。

http://jsfiddle.net/4L3kr052/1/

var getAvailableTags = function (combos, ids) {
        var matched = []
        combos.forEach(function(comb){
            var keys = comb.Combo.map(function(d){
                return d.Id;
            }); 
            var found = 1;
            ids.forEach(function(id){
                found &= (keys.indexOf(id) !== -1);
            });
            if (found){
                matched.push(comb);
            }

        })
        return matched;
}

【讨论】:

    【解决方案2】:

    要获得所需的组合,您需要 filter 它们,以便给定的组合包含 ids 数组中的每个 id。

    var combos = [{
          "Combo": [
            {
              "Id": 1,
              "Name": "Tag1"
            },
            {
              "Id": 3,
              "Name": "Tag3"
            }
          ]
        },
        {
          "Combo": [
            {
              "Id": 2,
              "Name": "Tag2"
            },
            {
              "Id": 3,
              "Name": "Tag3"
            },
            {
              "Id": 4,
              "Name": "Tag4"
            }
          ]
        }];
    
    function getCombos(combos, ids) {
        return combos.filter( // filter accepts combos that...
            function (g) {
                return ids.every( // ... contain every id in ids...
                    function (id) {
                        return g.Combo.some( // ... such that the id is present within some combo.
                            function (c) {
                                return c.Id === id;
                            });
                    });
    
            });
    }
    
    getCombos(combos, [3, 2]); // returns your desired combo
    getCombos(combos, [3, 1]); // returns the first combo
    getCombos(combos, [3, 5]); // returns an empty array
    

    【讨论】:

      【解决方案3】:

      一次解决一个问题,

      首先,

      如何为每个 id 测试一个 Combo?例如,将测试逻辑移动到它自己的函数中,以使您的生活更轻松

      function comboHasIds(combo, ids) {
          var i, j;
          find_next: for (i = 0; i < ids.length; ++i) {
              for (j = 0; j < combo.length; ++j)
                  if (combo[j].Id === ids[i])
                      continue find_next;
              return false; // if we reach here then id[i] wasn't in combo
          }
          return true; // if we reach here then we ran out of ids to test for
      }
      

      请注意使用label 让嵌套循环continue 外循环

      单个Combo上的用法示例如下

      var ex = [
              {"Id": 2, "Name": "Tag2"},
              {"Id": 3, "Name": "Tag3"},
              {"Id": 4, "Name": "Tag4"}
          ];
      
      comboHasIds(ex, [3, 1]); // false
      comboHasIds(ex, [3, 2]); // true
      

      正如zerkms points outAdeel's answer 一样,这个测试的形式是

      1. 对于每个 id
      2. 如果combo中有一些
      3. 具有 Id 属性 id
      4. 返回true

      可以使用 Array.prototype 方法 everysome 而不是嵌套循环来编写,例如作为arrow functions中的一行

      var comboHasIds = (combo, ids) => ids.every(id => combo.some(item => item.Id === id));
      

      第二,

      如何用这个测试迭代每个 Combo?我们为此提供了方便的 Array.prototype.filter 方法,因此使用上面的过滤看起来像

      // var data = /* your data from above */;
      var test = function (ids) {
              return function (e) {return comboHasIds(e.Combo, ids);};
          };
      
      data.filter(test([3, 2]));
      // [{"Combo": [
      //   {"Id": 2,"Name": "Tag2"},
      //   {"Id": 3,"Name": "Tag3"},
      //   {"Id": 4, "Name": "Tag4"}
      // ]}]
      

      【讨论】:

      • 他们说“让你的生活更轻松”,然后使用标签和 goto-like continue。这很有趣:-)
      • @zerkms 你更喜欢我写“例如”吗?这个答案的关键是OP可以将大问题分解成两个更容易解决的小问题。
      • 不知道确切更好的措辞,整个功能看起来太纠结了,即使是例如。 PS:es2015 oneliner:ids.every(id =&gt; combo.some(c =&gt; c.id === id))
      • @zerkms 对其进行了编辑以显示这种替代方式。不过说真的,使用breakcontinue 有那么高级吗?如果你没有学会循环,你就没有学会循环。不过,我可以理解标签对某些人来说是新的。现在 ES6 let 具有块作用域,它们可能会被更多地使用。
      • 不是continuebreak 有问题,而是无条件跳转。
      猜你喜欢
      • 1970-01-01
      • 2019-02-22
      • 1970-01-01
      • 2022-08-11
      • 2016-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-22
      相关资源
      最近更新 更多