Reputation: 838
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
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
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
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
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
Reputation: 17616
Using Array#from, Array#reduce, Array#concat, destructuring, spread syntax and 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
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
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