Reputation: 59
I am using js and have an dynamic array of object from an response which looks like the following:
[
{fromCountry: "TE", toCountry: "FE", type: "new", status: "created"},
{fromCountry: "TE", toCountry: "FE", type: "old", status: "cold"},
{fromCountry: "CD", toCountry: "EG", type: "used", status: "hot"},
{fromCountry: "CD", toCountry: "EG", type: "old", status: "hot"},
{fromCountry: "CD", toCountry: "EG", type: "old", status: "cold"}
];
I would like to create a new array and therefore I want to group them by fromCountry, count the fromCountry and add a nested array with the types and the value. It should look like is:
[
{ name: "TE", value: 2, child: [{ name: "new", value: 1 }, { name: "old", value: 1 }]},
{ name: "CD", value: 3, child: [{ name: "used", value: 1}, { name: "old", value: 2 }]}
]
(Note: I would not like to use extra javascript libraries)
Could you help me please?
Upvotes: 1
Views: 483
Reputation: 12884
let arr = [
{fromCountry:"TE",toCountry:"FE",type:"new",status:"created"},
{fromCountry:"TE",toCountry:"FE",type:"old",status:"cold"},
{fromCountry:"CD",toCountry:"EG",type:"used",status:"hot"},
{fromCountry:"CD",toCountry:"EG",type:"old",status:"hot"},
{fromCountry:"CD",toCountry:"EG",type:"old",status:"cold"}
];
let answer = [];
arr.forEach(x=> {
if(!answer.some(y => y.name === x.fromCountry)){
let newAnswer = {};
newAnswer.name = x.fromCountry;
newAnswer.value = 1;
newAnswer.child = [];
let child = {name: x.type, value: 1};
newAnswer.child.push(child);
answer.push(newAnswer);
}else{
let existAnswer = answer.find(y=>y.name === x.fromCountry);
existAnswer.value++;
if(existAnswer.child.some(z=>z.name === x.type)){
let childObj = existAnswer.child.find(z=>z.name === x.type);
childObj.value++;
}else{
let newChildObj = {name: x.type, value: 1}
existAnswer.child.push(newChildObj);
}
}
})
console.log(answer)
We loop thru the whole array, find the existence of element, if found we increment, if not found we push a new object. And same logic applies to child Obj.
This may not be elegant way but I believe is much easier to understand. Thou it's very subjective.
Upvotes: 1
Reputation: 386680
You could use a function for an arbitrary count of groups.
This proposal features an object which keeps the single object as part result, the grouping result and the hash (the value of the given key) and takes it as an accessor for the group.
The underscore property _
is necessary to separate a playload object from the hashing function of the object.
A possible needed children array is created on the fly.
The result is a nested structure which has the same depth as the given groups array.
function getGouped(array, groups) {
var result = [],
object = { _: { children: result } };
array.forEach(function (a) {
groups.reduce(function (r, k) {
var name = a[k];
if (!r[name]) {
r[name] = { _: { name, count: 0 } };
r._.children = r._.children || [];
r._.children.push(r[name]._);
}
r[name]._.count++;
return r[name];
}, object);
});
return result;
}
var data = [{ fromCountry: "TE", toCountry: "FE", type: "new", status: "created" }, { fromCountry: "TE", toCountry: "FE", type: "old", status: "cold" }, { fromCountry: "CD", toCountry: "EG", type: "used", status: "hot" }, { fromCountry: "CD", toCountry: "EG", type: "old", status: "hot" }, { fromCountry: "CD", toCountry: "EG", type: "old", status: "cold" }];
console.log(getGouped(data, ['fromCountry', 'type']));
console.log(getGouped(data, ['fromCountry', 'type', 'status']));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 1
Reputation: 26844
You can use reduce
to group the array into an object. Use Object.values
to convert the object into an array.
var arr = [{fromCountry:"TE",toCountry:"FE",type:"new",status:"created"},{fromCountry:"TE",toCountry:"FE",type:"old",status:"cold"},{fromCountry:"CD",toCountry:"EG",type:"used",status:"hot"},{fromCountry:"CD",toCountry:"EG",type:"old",status:"hot"},{fromCountry:"CD",toCountry:"EG",type:"old",status:"cold"}];
var result = Object.values(arr.reduce((c, {fromCountry,type}) => {
c[fromCountry] = c[fromCountry] || {name: fromCountry,value: 0,child: {}};
c[fromCountry].child[type] = c[fromCountry].child[type] || {name: type,value: 0};
c[fromCountry].child[type].value++;
c[fromCountry].value++;
return c;
}, {})).map(o => {
o.child = Object.values(o.child);
return o;
});
console.log(result);
Upvotes: 1
Reputation: 7291
Here is a slow old way of doing it, it makes temp objects and increments the values based on what it sees.
I'm sure there is a much faster way to do it so will be keeping an eye out for other answers.
I've commented the code but feel free to ask if something doesn't make sense.
var objs = [{fromCountry:"TE",toCountry:"FE",type:"new",status:"created"},{fromCountry:"TE",toCountry:"FE",type:"old",status:"cold"},{fromCountry:"CD",toCountry:"EG",type:"used",status:"hot"},{fromCountry:"CD",toCountry:"EG",type:"old",status:"hot"},{fromCountry:"CD",toCountry:"EG",type:"old",status:"cold"}];
// temp object and output array
let holding = {};
let output = [];
// for each object
for (let obj of objs) {
// if it's the first time we've seen this fromCountry make it
// and give it a value of 0
if (!holding[obj.fromCountry]) holding[obj.fromCountry] = {
value: 0
};
// if it's the first time we've seen this type make it and
// give it a value of 0
if (!holding[obj.fromCountry][obj.type]) holding[obj.fromCountry][obj.type] = {
value: 0
};
// increment values
holding[obj.fromCountry].value++;
holding[obj.fromCountry][obj.type].value++
}
// Now we need to reformat the object
// for each key in the holding object
for (let key of Object.keys(holding)) {
// make a new temp object in the right format
let temp = {
name: key,
value: holding[key].value,
child: []
};
// for each inner key in the holding object
for (let innerKey of Object.keys(holding[key])) {
// skip over value
if (innerKey == "value") continue
// make another temp object to be pushed to child
let innertemp = {
name: innerKey,
value: holding[key][innerKey].value
}
// push inner temp object to child
temp.child.push(innertemp);
}
//push whole temp object, now formatted correctly, to the output array
output.push(temp);
}
console.log(output);
I hope this is helpful 🙂
Upvotes: 1