按键对对象数组进行分组

时间:2015-04-27 16:47:19

标签: javascript arrays object grouping

我有这个对象数组,我希望它们按照特定的键分组,在这种情况下tags

var items = [
    {id: 0, tags: ["a"], name: "foo"},
    {id: 1, tags: [], name: "bar"},
    {id: 2, tags: ["a"], name: "bazz"},
    {id: 3, tags: ["b"], name: "wah"},
    {id: 4, tags: ["c"], name: "ikr"},
    {id: 5, tags: ["a"], name: "wtf"},
    {id: 6, tags: ["a","b"], name: "gtg"},
    {id: 7, tags: ["c"], name: "afk"}
]

所以我使用了下划线,就像这样:

var groupItems = _.groupBy(items, function(obj) {
  return obj.tags;
});

问题在于:

{
"a": [
        {"id": 0,"tags": ["a"],"name": "foo"},
        {"id": 2,"tags": ["a"],"name": "bazz"},
        {"id": 5,"tags": ["a"],"name": "wtf"}
     ],
"":  [
        {"id": 1,"tags": [],"name": "bar"}
     ],
"b": [
         {"id": 3,"tags": ["b"],"name": "wah"}
     ],
"c": [
         {"id": 4,"tags": ["c"],"name": "ikr"},
         {"id": 7,"tags": ["c"],"name": "afk"}
     ],
"a,b": [
         {"id": 6,"tags": ["a","b"],"name": "gtg"}
     ]

}

如果您还注意到,那些拥有多个标签的人会从数组joined创建一个tags密钥,这是一个非常不理想的结果。我怎样才能按tags对它们进行分组,并在数据有多个tags

时复制数据

4 个答案:

答案 0 :(得分:1)

假设当某个项目包含多个标记时,您希望每个标记下都有重复的条目_.groupBy将无法执行您想要的操作,因为它只是简单地拆分了一个集合(赢得了重复的条目)。

相反,您需要手动循环您的商品,然后是每个商品的标签,并为您建立清单(如果需要,可以使用_.each):

var items = [
    {id: 0, tags: ["a"], name: "foo"},
    {id: 1, tags: [], name: "bar"},
    {id: 2, tags: ["a"], name: "bazz"},
    {id: 3, tags: ["b"], name: "wah"},
    {id: 4, tags: ["c"], name: "ikr"},
    {id: 5, tags: ["a"], name: "wtf"},
    {id: 6, tags: ["a","b"], name: "gtg"},
    {id: 7, tags: ["c"], name: "afk"}
];

var groupItems = {};
for (var i = 0, item; item = items[i]; i++) {
  for (var t = 0, tag; tag = item.tags[t]; t++) {
    // If we do not have an array for our tag, add one
    groupItems[tag] = groupItems[tag] || [];
    // Push out item onto the tag's list in our groupItems
    groupItems[tag].push(item);
  }
}

哪个会给你:

{
  "a": [
    {"id": 0,"tags": ["a"],"name": "foo"},
    {"id": 2,"tags": ["a"],"name": "bazz"},
    {"id": 5,"tags": ["a"],"name": "wtf"},
    {"id": 6,"tags": ["a","b"],"name": "gtg"}
  ],
  "b": [
    {"id": 3,"tags": ["b"],"name": "wah"},
    {"id": 6,"tags": ["a","b"],"name": "gtg"}
  ],
  "c": [
    {"id": 4,"tags": ["c"],"name": "ikr"},
    {"id": 7,"tags": ["c"],"name": "afk"}
  ]
}

答案 1 :(得分:1)

我根据您的需求创建了一个解决方案,重视可读性和维护

http://jsfiddle.net/6x6vvukq/

var items = [
    {id: 0, tags: ["a"], name: "foo"},
    {id: 1, tags: [], name: "bar"},
    {id: 2, tags: ["a"], name: "bazz"},
    {id: 3, tags: ["b"], name: "wah"},
    {id: 4, tags: ["c"], name: "ikr"},
    {id: 5, tags: ["a"], name: "wtf"},
    {id: 6, tags: ["a"], name: "gtg"},
    {id: 7, tags: ["c"], name: "afk"}
]

function groupByTags(items){

    var grouped = {};    

    items.forEach(function(item){   
        var tags = item.tags;
        if(!tags.length) tags.push("");

        tags.forEach(function(tag){

            if(tag in grouped == false){
                grouped[tag] = [];
            }
            grouped[tag].push(item);

        });        
    });    

    return grouped;

}

console.log(groupByTags(items));

答案 2 :(得分:1)

这是一个正确处理空标记数组的小函数。您可能希望将forEach替换为for循环以获得更好的性能,并添加一些类型检查以使此功能更通用且更不容易出错。

function groupBy(arr, propName) {
    var grouped = {};

    arr.forEach(function (thing) {
        var values = thing[propName];

        if (!values.length) {
            grouped[''] = grouped[''] || [];
            grouped[''].push(thing);

            return;
        }

        values.forEach(function (value) {
            grouped[value] = grouped[value] || [];
            grouped[value].push(thing)
        });
    });

    return grouped;
}

http://jsfiddle.net/cscrak32/

答案 3 :(得分:0)

reduce

的另一种变体
items.reduce(function(acc, el) {
  (el.tags.length === 0 ? [""] : el.tags).forEach(function(el2) {
    if (!acc[el2]) acc[el2] = [];
    acc[el2].push(el);
  });
  return acc;
}, {});

var items = [{
  id: 0,
  tags: ["a"],
  name: "foo"
}, {
  id: 1,
  tags: [],
  name: "bar"
}, {
  id: 2,
  tags: ["a"],
  name: "bazz"
}, {
  id: 3,
  tags: ["b"],
  name: "wah"
}, {
  id: 4,
  tags: ["c"],
  name: "ikr"
}, {
  id: 5,
  tags: ["a"],
  name: "wtf"
}, {
  id: 6,
  tags: ["a", "b"],
  name: "gtg"
}, {
  id: 7,
  tags: ["c"],
  name: "afk"
}]

var result = items.reduce(function(acc, el) {
  (el.tags.length === 0 ? [""] : el.tags).forEach(function(el2) {
    if (!acc[el2]) acc[el2] = [];
    acc[el2].push(el);
  });
  return acc;
}, {});

document.getElementById('res').innerHTML = JSON.stringify(result, null, 2)
<pre id="res"></pre>