0

I have an array of objects that each contain two categories, one of these categories represents a group.

[
  {
    "uuid": 123,
    "group": "test_group"
  },
  {
    "uuid": 321,
    "group": "test_group"
  },
  {
    "uuid": 432,
    "group": "test_group2"
  }
]

I'm looking to generate a JSON response that has categorized them by their groups.

{
  "objects": [
    {
      "group": "test_group",
      "items": [
        {
          "uuid": 123
        },
        {
          "uuid": 321
        }
      ]
    },
    {
      "group": "test_group2",
      "items": [
        {
          "uuid": 432
        }
      ]
    }
  ]
}

At the moment I've accomplished this by first iterating over and creating a set of all of the groups involved, and then iterating again and grouping them appropriately. I was wondering if there was a more succinct way of doing this, perhaps using some of the new operators introduced in ES6.

FredLoh
  • 1,804
  • 18
  • 27
  • 4
    There's nothing specific in ES6 for this kind of data reshaping. You can take an ES5 solution and make it more concise and readable using arrow functions, spread operators, deconstruction, and maybe even the `Array#find` method. –  Dec 11 '16 at 14:33
  • This question is too broad, because this task can be accomplished in lots of different ways. Try to find a solution by yourself, and if you encounter any specific problem, then ask. – Michał Perłakowski Dec 11 '16 at 14:47
  • I mentioned that I had a solution, was asking on here to see if anybody had a different take on it... – FredLoh Dec 11 '16 at 14:50

2 Answers2

3

Iterate using Array#reduce, and collect the items by group into a Map. Use spread to convert the Map#values back into array:

const data = [
  {
    "uuid": 123,
    "group": "test_group"
  },
  {
    "uuid": 321,
    "group": "test_group"
  },
  {
    "uuid": 432,
    "group": "test_group2"
  }
];

const result = [...data.reduce((hash, { uuid, group }) => {
  const current = hash.get(group) || { group, items: [] };
  
  current.items.push({ uuid });
  
  return hash.set(group, current);
}, new Map).values()];

console.log(result);
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • Did you omit the parentheses for `new Map` on purpose? Also, I think it's better to use `Array.from()` instead of the spread syntax to convert an iterable object to an array, because it's more explicit this way. – Michał Perłakowski Dec 11 '16 at 14:51
  • Of course. They are [optional](http://stackoverflow.com/a/3034952/5157454). I like `Array.from()`, but with spread you can see the array, so I'm not sure what's more explicit. – Ori Drori Dec 11 '16 at 14:54
  • @OriDrori what is the reason for the new Map at the end? I'm following it up to that point and then get lost. Why is it needed? – FredLoh Dec 11 '16 at 15:53
  • Look at the `Array#reduce` documentation. This is the `initialValue`, and it sets the `accumulator` (`hash` in my code) in the callback in the 1st iteration. – Ori Drori Dec 11 '16 at 15:57
0

I don't know of there is a some new operator or an array function to make this easier introduced in ES6, however you could should be able to do it in a single iteration:

var arrayLength = myArray.length;
var response = {objects: []};
var groupIndex = {};

for (var i = 0; i < arrayLength; i++) {
  let group = myArray[i].group;
  if (!groupIndex.hasOwnProperty(group)) {
    groupIndex[group] = groupIndex.length;
    response.objects.push({
      group: group,
      items: [
        {uuid: myArray[i].uuid}
      ]
  }
  else {
    let index = groupIndex[group];
    response.objects[index].items.push({uuid: myArray[i].uuid});
  }
}
Елин Й.
  • 953
  • 10
  • 25