RITZ XAVI
RITZ XAVI

Reputation: 3789

Convert an array of json objects in to another json object

I have an array of json objects as shown below.

[
    {
      "wrappedItem": {
        "systemName": "jira",
        "domainObject": "issue",
        "eventType": "creation"
      },
      "id": 39,
      "enabled": true
    },
    {
      "wrappedItem": {
        "systemName": "jira",
        "domainObject": "issue",
        "eventType": "predeletion"
      },
      "id": 40,
      "enabled": true
    },
    {
      "wrappedItem": {
        "systemName": "jira",
        "domainObject": "issue",
        "eventType": "deletion"
      },
      "id": 41,
      "enabled": true
    },
    {
      "wrappedItem": {
        "systemName": "jira",
        "domainObject": "issue",
        "eventType": "update"
      },
      "id": 42,
      "enabled": true
    },
    {
      "wrappedItem": {
        "systemName": "jira",
        "domainObject": "recquirement",
        "eventType": "new"
      },
      "id": 43,
      "enabled": true
    },
    {
      "wrappedItem": {
        "systemName": "jira",
        "domainObject": "recquirement",
        "eventType": "old"
      },
      "id": 44,
      "enabled": true
    },
    {
      "wrappedItem": {
        "systemName": "bitbucket",
        "domainObject": "branch",
        "eventType": "creation"
      },
      "id": 45,
      "enabled": true
    },
    {
      "wrappedItem": {
        "systemName": "bitbucket",
        "domainObject": "branch",
        "eventType": "deletion"
      },
      "id": 46,
      "enabled": true
    },
    {
      "wrappedItem": {
        "systemName": "bitbucket",
        "domainObject": "pull-request",
        "eventType": "creation"
      },
      "id": 47,
      "enabled": true
    }
  ]

If you closely observe the data above, you can see that the data can be grouped based on the systemName, followed by the domainObject, followed by eventType.

I want to display the data into a tree like fashion. For this, I have decided to use angular-ui-tree third party library. It expects the json object to be in a certain way. (Please go to that link to see the structure of the json object it needs).

I have a requirement where I have to convert my data into a structure needed by the angular-ui-tree so that I can display my data as a tree.

Eventually, I am expecting the json object to look like :

[
  {
    "systemName": "jira",
    "domains": [
      {
        "domainObject": "issue",
        "eventTypes": [
          {
            "eventType": "creation",
            "id": 39,
            "enabled": true
          },
          {
            "eventType": "pre-deletion",
            "id": 40,
            "enabled": true
          },
          {
            "eventType": "deletion",
            "id": 41,
            "enabled": true
          },
          {
            "eventType": "issue",
            "id": 42,
            "enabled": true
          }
        ]
      },
      {
        "domainObject": "requirement",
        "eventTypes": [
          {
            "eventType": "new",
            "id": 43,
            "enabled": true
          },
          {
            "eventType": "old",
            "id": 44,
            "enabled": true
          }
        ]
      }
    ]
  },
  {
    "systemName": "bitbucket",
    "domains": [
      {
        "domainObject": "branch",
        "eventTypes": [
          {
            "eventType": "creation",
            "id": 45,
            "enabled": true
          },
          {
            "eventType": "deletion",
            "id": 46,
            "enabled": true
          }
        ]
      },
      {
        "domainObject": "pull-request",
        "eventTypes": [
          {
            "eventType": "creation",
            "id": 47,
            "enabled": true
          }
        ]
      }
    ]
  }
]  

As you can see this data is grouped by systemName, followed by domainObject and followed by eventType. Can someone help me in converting my data in the structure shown above.

Upvotes: 0

Views: 204

Answers (2)

Nina Scholz
Nina Scholz

Reputation: 386550

You could use a nested hash table and group the items with the given keys.

Basically this proposal works for various length of grouping properties. In this case it groups by

groups = {
    'wrappedItem.systemName': 'domains',
    'wrappedItem.domainObject': 'eventTypes'
}

Then it need a function for building the final object

{
    eventType: "creation",
    id: 39,
    enabled: true
}

at the end of the nested grouping with

fn = function (o) {
    return { eventType: o.wrappedItem.eventType, id: o.id, enabled: o.enabled };
}

The function getGroupedData, expects a data array, a grouping object and callback for generating an inner item.

The key functionaliy is an object, which holds an array for each level of groups.

On start, the hash table has one property and the reference to the result array

{
    _: result
}

In the first loop of the data array and by iterating all groups, it gets a new level for each group.

First it gets the value of the first group property, which is 'jira'. It is taken for a check if the property exist in the hash table. 'jira' does actually not exist and a new object is generated with the same pattern as the original hash table.

{
    _: result
    jira: { _: [] }
}

And another object is generated for the result set

{
    systemName: "jira",
    domains": []
}

and added to the result

{
    _: [
        {
            systemName: "jira",
            domains: [] // <---+
        }               //     | domains and jira._ 
    ],                  //     | share the same 
    jira: {             //     | reference to the array
        _: []           // <---+
    }
}

Why do I need two different reference to the result array?

Because you need a special data structure for the result. It is array oriented, but the access is done via indices. The other structure has a direct access by a property, which is easier to access, without more methods.

For the next group, the object of jira is returned and another check is performed, if in the actual object is a property with the name 'issue'. This is obviously in the first loop not the case and added. The actual object looks like

{
    _: [
        {
            domainObject: "issue",
            eventTypes: []
        }
    ],
    issue: {
        _: []
    }
}

And _[0].eventTypes and issue._ shares again the same reference to an array.

Now at the end of the first loop of the data, the hash object looks like that

{                                                  //   + same objects
    _: [                                           //               -+ result set
        {                                          //                |
            systemName: "jira",                    //                |
            domains: [                             //                |
                {                                  //         +      |
                    domainObject: "issue",         //         +      |
                    eventTypes: [                  //         +      |
                        {                          //    +    +      |
                            eventType: "creation", //    +    +      |
                            id: 39,                //    +    +      |
                            enabled: true          //    +    +      |
                        }                          //    +    +      |
                    ]                              //         +      |
                }                                  //         +      |
            ]                                      //                |
        }                                          //               -+
    ],                                             //
    jira: {                                        //
        _: [                                       //
            {                                      //         +
                domainObject: "issue",             //         +
                eventTypes: [                      //         +
                    {                              //    +    +
                        eventType: "creation",     //    +    +
                        id: 39,                    //    +    +
                        enabled: true              //    +    +
                    }                              //    +    +
                ]                                  //         +
            }                                      //         +
        ],                                         //
        issue: {                                   //
            _: [                                   //
                {                                  //    +
                    eventType: "creation",         //    +
                    id: 39,                        //    +
                    enabled: true                  //    +
                }                                  //    +
            ]                                      //
        }                                          //
    }                                              //
}                                                  //

The data array is looped to the end an all groupes are now in the object and all grouped arrays have been created. Then only the result set is returned.

function getGroupedData(data, groups, fn) {
    var keys = Object.keys(groups),
        result = [];

    data.forEach(function (a) {
        keys.reduce(function (r, k) {
            var temp,
                value = k.split('.').reduce(function (r, l) {
                    return (r || {})[l];
                }, a);
            if (!r[value]) {
                r[value] = { _: [] };
                temp = {};
                temp[k.split('.').pop()] = value;
                temp[groups[k]] = r[value]._;
                r._.push(temp);
            }
            return r[value];
        }, this)._.push(fn(a));
    }, { _: result });
    return result;
}


var data = [{ wrappedItem: { systemName: "jira", domainObject: "issue", eventType: "creation" }, "id": 39, enabled: true }, { wrappedItem: { systemName: "jira", domainObject: "issue", eventType: "predeletion" }, "id": 40, enabled: true }, { wrappedItem: { systemName: "jira", domainObject: "issue", eventType: "deletion" }, "id": 41, enabled: true }, { wrappedItem: { systemName: "jira", domainObject: "issue", eventType: "update" }, "id": 42, enabled: true }, { wrappedItem: { systemName: "jira", domainObject: "recquirement", eventType: "new" }, "id": 43, enabled: true }, { wrappedItem: { systemName: "jira", domainObject: "recquirement", eventType: "old" }, "id": 44, enabled: true }, { wrappedItem: { systemName: "bitbucket", domainObject: "branch", eventType: "creation" }, "id": 45, enabled: true }, { wrappedItem: { systemName: "bitbucket", domainObject: "branch", eventType: "deletion" }, "id": 46, enabled: true }, { wrappedItem: { systemName: "bitbucket", domainObject: "pull-request", eventType: "creation" }, "id": 47, enabled: true }],
    groups = { 'wrappedItem.systemName': 'domains', 'wrappedItem.domainObject': 'eventTypes' },
    fn = function (o) {
        return { eventType: o.wrappedItem.eventType, id: o.id, enabled: o.enabled };
    };

console.log(getGroupedData(data, groups, fn));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 2

ibrahim mahrir
ibrahim mahrir

Reputation: 31682

Use reduce to hash the object and their subobject, then use Object.keys with map to transform those hash object into an array like this:

function group(arr) {
    // PHASE 1: wrap the object and the sub-objects into hash objects (group them)
    var hash = arr.reduce(function(h, o) {                                // for each object o in the array arr
        var sn = o.wrappedItem.systemName;                                // get the systemName
        var dob = o.wrappedItem.domainObject;                             // get the domainObject
        var ev = {eventType: o.wrappedItem.eventType, id: o.id, enabled: o.enabled}; // create an eventType object 
        if(h[sn]) {                                                       // if the systemName sn is already hashed
            if(h[sn].domainHash[dob])                                     // if the domain object dob is already hashed
                h[sn].domainHash[dob].eventTypes.push(ev);                // then push the eventType ev into its eventTypes array
            else                                                          // if not (the domain object is not hashed yet) then create a new domain object that have the eventType ev as the only item in its eventTypes array
                h[sn].domainHash[dob] = {domainObject: dob, eventTypes: [ev]};
        }
        else {                                                            // if not (the systemName sn is not hashed yet)
            h[sn] = {systemName: sn, domainHash: {}};                     // create a new systemName object with its domainHash object initialized with the domain object dob which is also initalized with the eventType ev
            h[sn].domainHash[dob] = {domainObject: dob, eventTypes: [ev]};
        }

        return h;
    }, {});

    // PHASE 2: unwrap the hash objects
    return Object.keys(hash).map(function(o) {                            // unwrap hash
        var domains = Object.keys(hash[o].domainHash).map(function(d) {   // unwrap domainHash
            return hash[o].domainHash[d];
        });
        delete hash[o].domainHash;                                        // remove the property domainHash
        hash[o].domains = domains;                                        // replace it with the property domains
        return hash[o];
    });
}

Example:

var array = [{"wrappedItem":{"systemName":"jira","domainObject":"issue","eventType":"creation"},"id":39,"enabled":true},{"wrappedItem":{"systemName":"jira","domainObject":"issue","eventType":"predeletion"},"id":40,"enabled":true},{"wrappedItem":{"systemName":"jira","domainObject":"issue","eventType":"deletion"},"id":41,"enabled":true},{"wrappedItem":{"systemName":"jira","domainObject":"issue","eventType":"update"},"id":42,"enabled":true},{"wrappedItem":{"systemName":"jira","domainObject":"recquirement","eventType":"new"},"id":43,"enabled":true},{"wrappedItem":{"systemName":"jira","domainObject":"recquirement","eventType":"old"},"id":44,"enabled":true},{"wrappedItem":{"systemName":"bitbucket","domainObject":"branch","eventType":"creation"},"id":45,"enabled":true},{"wrappedItem":{"systemName":"bitbucket","domainObject":"branch","eventType":"deletion"},"id":46,"enabled":true},{"wrappedItem":{"systemName":"bitbucket","domainObject":"pull-request","eventType":"creation"},"id":47,"enabled":true}];

function group(arr) {
    var hash = arr.reduce(function(h, o) {
        var sn = o.wrappedItem.systemName;
        var dob = o.wrappedItem.domainObject;
        var ev = {eventType: o.wrappedItem.eventType, id: o.id, enabled: o.enabled};
        if(h[sn]) {
            if(h[sn].domainHash[dob])
                h[sn].domainHash[dob].eventTypes.push(ev);
            else
                h[sn].domainHash[dob] = {domainObject: dob, eventTypes: [ev]};
        }
        else {
            h[sn] = {systemName: sn, domainHash: {}};
            h[sn].domainHash[dob] = {domainObject: dob, eventTypes: [ev]};
        }

        return h;
    }, {});

    return Object.keys(hash).map(function(o) {
        var domains = Object.keys(hash[o].domainHash).map(function(d) {
            return hash[o].domainHash[d];
        });
        delete hash[o].domainHash;
        hash[o].domains = domains;
        return hash[o];
    });
}


console.log(group(array));

Explanation:

After phase 1, we get an object like this:

{
  "jira": {
    systemName: "jira",
    domainHash: {
      "issue": {
        domainObject: "issue",
        eventTypes: [...]
      },
      "other domain object": {
        domainObject: "other domain object",
        eventTypes: [...]
      },
      ...
    }
  },
  "other system name": {
    systemName: "other system name",
    domainHash: {
      "something": {
        domainObject: "something",
        eventTypes: [...]
      },
      ...
    }
  },
  ...
}

Which is not the output you want, the hashing makes it easier to group items, but the output will be an object. So to get the desired result (an array), you'll have too map the sub-object of the hash object into an array.

var result = {
  hash["jira"],
  hash["other system name"],
  ...
];

And not just the systemName objects that need mapping, the domainObject objects need to be mapped too (for each systemName object):

var domains = [
  domainHash["issue"],
  domainHash["other domain object"],
  ...
];

Upvotes: 2

Related Questions