Arman Feyzi
Arman Feyzi

Reputation: 838

Convert JSON format (Group by)

I have JSON object like this:

[{
    "name" : "cat",
    "value" : 17,
    "group" : "animal",
},
 {
    "name" : "dog",
    "value" : 6,
    "group" : "animal",
},
 {
    "name" : "snak",
    "value" : 2,
    "group" : "animal",
},
{
    "name" : "tesla",
    "value" : 11,
    "group" : "car",
},
{
    "name" : "bmw",
    "value" : 23,
    "group" : "car",
}]

I want to convert this JSON to below format by JS:

[{
  "name":"animal",
  "children":[
     {"name":"cat", "value":17},
     {"name":"dog", "value":6},
     {"name":"snak", "value":2}
]},
{
  "name":"car",
  "children":[
     {"name":"bmw", "value":11},
     {"name":"tesla", "value":23}
]}]

I try to convert and filter by Reduce function but I couldn't convert to this format.

EDIT:

The code I tested was this.

let groupBy = function(xs, key) {
    return xs.reduce(function(rv, x) {
    (rv[x[key]] = rv[x[key]] || []).push(x);
    return rv;
    }, {});
};

let groubedByExchange=groupBy(JSON_data, 'group');

Upvotes: 6

Views: 19079

Answers (7)

Bonjov
Bonjov

Reputation: 305

const transform = things =>
  things.reduce((acc, { name, value, group }) => {
    const existingGroup = acc.find(g => g.name === group) || {};
    return [
      ...acc.filter(g => g.name !== group),
      {
        ...existingGroup,
        name: group,
        children: [...(existingGroup.children || []), { name, value }],
      },
    ];
  }, []);

Upvotes: 1

jo_va
jo_va

Reputation: 13973

A simple solution is to build an intermediate dictonary and then transform it to your output structure.

You can use Array.reduce(), Object.entries() and Array.map() to do it:

const data = [
  { "name" : "cat", "value" : 17, "group" : "animal" },
  { "name" : "dog", "value" : 6, "group" : "animal" },
  { "name" : "snak", "value" : 2, "group" : "animal" },
  { "name" : "tesla", "value" : 11, "group" : "car" },
  { "name" : "bmw", "value" : 23, "group" : "car" }
];

const result = Object.entries(data.reduce((acc, { name, value, group }) => {
  acc[group] = (acc[group] || []);
  acc[group].push({ name, value });
  return acc;
}, {})).map(([key, value]) => ({ name: key, children: value }));

console.log(result);

Using the spread operator makes it one line shorter and still readable:

const data = [
  { "name" : "cat", "value" : 17, "group" : "animal" },
  { "name" : "dog", "value" : 6, "group" : "animal" },
  { "name" : "snak", "value" : 2, "group" : "animal" },
  { "name" : "tesla", "value" : 11, "group" : "car" },
  { "name" : "bmw", "value" : 23, "group" : "car" }
];

const result = Object.entries(data.reduce((acc, { name, value, group }) => {
  acc[group] = [...(acc[group] || []), { name, value }];
  return acc;
}, {})).map(([key, value]) => ({ name: key, children: value }));

console.log(result);

And one liner shorter with the comma operator:

const data = [
  { "name" : "cat", "value" : 17, "group" : "animal" },
  { "name" : "dog", "value" : 6, "group" : "animal" },
  { "name" : "snak", "value" : 2, "group" : "animal" },
  { "name" : "tesla", "value" : 11, "group" : "car" },
  { "name" : "bmw", "value" : 23, "group" : "car" }
];

const result = Object.entries(data.reduce((acc, { name, value, group }) =>
  (acc[group] = [...(acc[group] || []), { name, value }], acc)
, {})).map(([key, value]) => ({ name: key, children: value }));

console.log(result);

The same can be done with Object.assign():

const data = [
  { "name" : "cat", "value" : 17, "group" : "animal" },
  { "name" : "dog", "value" : 6, "group" : "animal" },
  { "name" : "snak", "value" : 2, "group" : "animal" },
  { "name" : "tesla", "value" : 11, "group" : "car" },
  { "name" : "bmw", "value" : 23, "group" : "car" }
];

const result = Object.entries(data.reduce((acc, { name, value, group }) =>
  Object.assign(acc, { [group]: [...(acc[group] || []), { name, value }] })
, {})).map(([key, value]) => ({ name: key, children: value }));

console.log(result);

And finally, a bit longer but with a reusable groupBy function:

const data = [
  { "name" : "cat", "value" : 17, "group" : "animal" },
  { "name" : "dog", "value" : 6, "group" : "animal" },
  { "name" : "snak", "value" : 2, "group" : "animal" },
  { "name" : "tesla", "value" : 11, "group" : "car" },
  { "name" : "bmw", "value" : 23, "group" : "car" }
];

const groupBy = prop => data => {
  return data.reduce((dict, item) => {
    const { [prop]: _, ...rest } = item;
    dict[item[prop]] = [...(dict[item[prop]] || []), rest];
    return dict;
  }, {});
};

const result = Object.entries(groupBy('group')(data))
  .map(([key, value]) => ({ name: key, children: value }));

console.log(result);

Upvotes: 7

fabio.sussetto
fabio.sussetto

Reputation: 7065

This would be my ES6 solution, using map and reduce:

const data = [
  {
    name: "cat",
    value: 17,
    group: "animal"
  },
  {
    name: "dog",
    value: 6,
    group: "animal"
  },
  {
    name: "snak",
    value: 2,
    group: "animal"
  },
  {
    name: "tesla",
    value: 11,
    group: "car"
  },
  {
    name: "bmw",
    value: 23,
    group: "car"
  }
];

const grouped = data.reduce((acc, currItem) => {
  const groupKey = currItem.group;
  if (!acc[groupKey]) {
    acc[groupKey] = [currItem];
  } else {
    acc[groupKey].push(currItem);
  }
  return acc;
}, {});

const res = Object.keys(grouped).map(key => ({
  name: key,
  children: grouped[key].map(groupItem => ({
    name: groupItem.name,
    value: groupItem.value
  }))
}));

console.log(res);

Check the console output to see the intermediate results.

I think some of the other answers use an unnecessary find() (which is O(n)) while you can just test if a current group key is already in there in O(1).

I'm storing the grouped results in a intermediate variable grouped for clarity, but it can all be inlined.

Upvotes: 1

arunmmanoharan
arunmmanoharan

Reputation: 2675

Here you go. You can use lodash to achieve the same.

var data = [{
    "name": "cat",
    "value": 17,
    "group": "animal",
  },
  {
    "name": "dog",
    "value": 6,
    "group": "animal",
  },
  {
    "name": "snak",
    "value": 2,
    "group": "animal",
  },
  {
    "name": "tesla",
    "value": 11,
    "group": "car",
  },
  {
    "name": "bmw",
    "value": 23,
    "group": "car",
  }
]

var result = _(data)
  .groupBy('group')
  .map((group, name) =>
    ({
      name,
      children: _.map(group, ({
        name: name,
        value
      }) => ({
        name,
        value
      }))
    }))
  .value()

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

Upvotes: 2

kockburn
kockburn

Reputation: 17616

Using Array#from, Array#reduce, Array#concat, destructuring, spread syntax and Map.

  1. reorganize your data structure by using Map
  2. restructuring your data using .map

const data=[{"name":"cat","value":17,"group":"animal",},{"name":"dog","value":6,"group":"animal",},{"name":"snak","value":2,"group":"animal",},{"name":"tesla","value":11,"group":"car",},{"name":"bmw","value":23,"group":"car",}];

const res = Array.from(
  data.reduce((a,{group, ...rest})=>{
    return a.set(group, [rest].concat(a.get(group)||[]));
  }, new Map())
).map(([group, children])=>({group,children}));

console.log(res);

Upvotes: 4

Nina Scholz
Nina Scholz

Reputation: 386660

You could build an array and search for the same group in the array.

var array = [{ name: "cat", value: 17, group: "animal" }, { name: "dog", value: 6, group: "animal" }, { name: "snak", value: 2, group: "animal" }, { name: "tesla", value: 11, group: "car" }, { name: "bmw", value: 23, group: "car" }],
    result = array.reduce((r, { group: name, ...object }) => {
        var temp = r.find(o => o.name === name);
        if (!temp) r.push(temp = { name, children: [] });
        temp.children.push(object);
        return r;
    }, []);
    
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 8

fila90
fila90

Reputation: 1457

const arr = [{
    "name": "cat",
    "value": 17,
    "group": "animal",
  },
  {
    "name": "dog",
    "value": 6,
    "group": "animal",
  },
  {
    "name": "snak",
    "value": 2,
    "group": "animal",
  },
  {
    "name": "tesla",
    "value": 11,
    "group": "car",
  },
  {
    "name": "bmw",
    "value": 23,
    "group": "car",
  }
]

const newFormat = arr.reduce((pre, cur) => {
  const group = pre.find(grp => grp.name === cur.group)
  if (group) {
    group.children.push({
      name: cur.name,
      value: cur.value
    })
    return pre
  }

  const newGroup = {
    name: cur.group,
    children: [{
      name: cur.name,
      value: cur.value
    }]
  }
  pre.push(newGroup)
  return pre
}, [])

console.log(newFormat)

there you go. first you try to find that group in new array, if it exists you add that child to it. if not, you create the group and children array and push it to the array

Upvotes: 2

Related Questions