0

I have this array of objects, and I want them to group by their specific key, in this case 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"}
]

And so I used underscore, like so:

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

The problem with that is that:

{
"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"}
     ]

}

If you would also notice, those who has multiple tags, created a joined key from the array tags which is quite an undesirable result. How can I, group them by tags and duplicate the data when they have multiple tags

8
  • 2
    What you have is an array of objects, not JSON. JSON is a data-format, like XML or CSV. Commented Apr 27, 2015 at 16:51
  • Ah right, array of json of objects. Commented Apr 27, 2015 at 16:55
  • why your id:6 in result have "tags": ["a","b"] but in source tags: ["a"] Commented Apr 27, 2015 at 16:55
  • Ah, just a typo, I'll fix it Commented Apr 27, 2015 at 16:59
  • so what wrong with _.groupBy? Commented Apr 27, 2015 at 17:00

4 Answers 4

1

Assuming you want duplicate entries under each tag when an item has multiple tags _.groupBy will not do what you want as it simply splits a collection up (won't duplicate entries).

Instead, you will need to loop over your items manually, then each item's tags and build up your list for you (you could use _.each if you wanted):

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

Which will give you:

{
  "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"}
  ]
}
Sign up to request clarification or add additional context in comments.

Comments

1

I created a solution based on your needs, put importance on the readability and maintenance

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

Comments

1

Here is a little function that properly handles empty arrays of tags. You may want to replace forEach with for loop for better performance and add some type checks to make this function more generic and less error prone.

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/

Comments

0

Yet another variant with 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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.