sublimelaconic
sublimelaconic

Reputation: 116

How to group data with similar keys?

I have this data from CSV:

Group   Profession  Status          Count
6       Fisherman   Offer Accepted  1
6       Fisherman   All             1
7       Fisherman   Offer Accepted  1
7       Fisherman   All             1
8       Banker      Onboard         2
8       Banker      All             2
8       Cook        Onboard         4
8       Cook        All             4
8       Developer   Onboard         2
8       Developer   All             2
9       Banker      Onboard         2
9       Banker      Offer Accepted  1
9       Banker      All             3

Which I need to return as a JSON array:

"Fisherman" : {
    6 : {
        "Offer Accepted" : 1,
        "All" : 1
    },
    7 : {
        "Offer Accepted" : 1,
        "All" : 1
    }
},
"Banker" : {
    8 : {
        "Onboard" : 2,
        "All" : 2
    },
    9 : {
        "Onboard" : 2,
        "Offer Accepted" : 1,
        "All" : 3
    }
},
....so on

So far, what I did was I got all the unique Profession and Group.

Then I looped through all the data and compared if there's a match for Profession AND Group.

for(var d in data) {
    var json = [];
    for(var p in profession) {
        for(var g in group) {
            if(data[d]["Profession"] == profession[p] && data[d]["Group"] == group[g]) {
                json.push({data[d]["Status"] : data[d]["Count"]});
                // put 'json' variable in JSON array with key group? 
            }
        }
    }
}

If there is a match, I created an array wherein I pushed the Status and Count.

But I really don't know how to proceed from there.

Thank you for your help!

Upvotes: 1

Views: 77

Answers (4)

Nina Scholz
Nina Scholz

Reputation: 386670

Suppose data is an array with objects like,

{ Group: 6, Profession: 'Fisherman', Status: 'Offer Accepted', Count: 1 }

then you could use the following

var order = ['Profession', 'Group', 'Status'],
    object = {};

data.forEach(function (d) {
    order.reduce(function (r, a) {
        r[d[a]] = r[d[a]] || {};
        return r[d[a]];
    }, object).Count = d.Count;
});

How it works:

d is an object with the structure like above. oder is an array with keys in the wanted order for the result object. (I renamed json to object, because JSON is a string with a special formatting and not an object, like here necessary.)

For an assignment of count, it is necessary to know the path to the property. This is granted with iterating over the order for the keys of the actual object d.

r[d[a]] = r[d[a]] || {};

This d[a] is taken for a check if the property exits and if not to assign an empty object.

At the end of the callback, the reference to the last object r[d[a]] is returned.

At last, a new property Count is assinged with the value of d.Count

object                       a          d[a]           return value
---------------------------- ---------- -------------- ------------
{}                           Profession Fisherman      {}
                /--------------------------------------/            (same reference)
{ "Fisherman": {} }          Group      6              {}
                       /-------------------------------/            (same reference)
{ "Fisherman": { "6": {} } } Status     Offer Accepted {}

object after first loop of data

{
    "Fisherman": {
        "6": {
            "Offer Accepted": {
                "Count": 1
            }
        }
    }
}

Roundup: reduce returns something, which is highly controllable.

Upvotes: 2

Redu
Redu

Reputation: 26181

Assuming your data is in this shape, one solution can be produced by using Array.prototype.reduce() as follows;

var str = 'Group,Profession,Status,Count\n6,Fisherman,OfferAccepted,1\n6,Fisherman,All,1\n7,Fisherman,OfferAccepted,1\n7,Fisherman,All,1\n8,Banker,On Board,2\n8,Banker,All,2\n8,Cook,On Board,4\n8,Cook,All,4\n8,Developer,On Board,2\n8,Developer,All,2\n9,Banker,On Board,2\n9,Banker,OfferAccepted,1\n9,Banker,All,3',
   data = str.split("\n").map(s => s.split(",")).slice(1),
  jdata = data.reduce((p,c) => (p[c[1]] ? p[c[1]][c[0]] ? p[c[1]][c[0]][c[2]] = c[3]
                                                        : p[c[1]][c[0]] = {[c[2]]:c[3]}
                                        : p[c[1]] = {[c[0]]:{[c[2]]:c[3]}},p),{});

document.write("<pre>" + JSON.stringify(jdata,null,2) + "</pre>");

Upvotes: 0

sielakos
sielakos

Reputation: 2404

You can do it quite easy with reduce:

const result = data.reduce((result, row) => {
    ensureKeyExists(result, row.Profession);
  ensureKeyExists(result[row.Profession], row.Group);

  result[row.Profession][row.Group][row.Status] = row.Count;

  return result;
}, {});

function ensureKeyExists(object, key, defaultVal = {}) {
    if (!object[key]) {
    object[key] = defaultVal;
  }
}

Full example: https://jsfiddle.net/k1zk3ses/

Upvotes: 0

Mojtaba
Mojtaba

Reputation: 5002

Try this:

var result = new Array();
for(var d in data){
    if(result[d.profession] == 'undefined'){
        result[d.profession] = new Array();
    }
    if(result[d.profession][d.group] == 'undefined'){
        result[d.profession][d.group] = new Array();
    }
    result[d.profession][d.group][d.status] = d.count;
}

result = JSON.stringify(result);//convert array to json
console.log(result);

I didn't test it and I supposed your JSON data are in data variable

Upvotes: 0

Related Questions