严格过滤数组数组

Strict Filter Array of Arrays

我有一个标签输入框,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"
        }
      ]
    }]

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

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/

一次解决一个问题,

首先,

如何为每个 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 to let the nested loop 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 , as does ,这个测试的形式是

  1. 对于每个 id
  2. 如果combo
  3. 中有一些
  4. 其中有 Id 属性 id
  5. Return true

可以使用Array.prototype方法编写every and some instead of nested loops, e.g. as one line in 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"}
// ]}]

要获得所需的组合,您需要 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

我创建了一个 fiddle 来解决这个问题。

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;
}