简体   繁体   中英

group, filter and count in array

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?

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

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 🙂

 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 increament, 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.

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: "F​​E", type: "new", status: "created" }, { fromCountry: "TE", toCountry: "F​​E", type: "old", status: "cold" }, { fromCountry: "CD", toCountry: "E​​G", type: "used", status: "hot" }, { fromCountry: "CD", toCountry: "E​​G", 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; } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM