Thilak Raj
Thilak Raj

Reputation: 920

Convert object with nested objects into array of objects,re- groupped by inner object's keys

I have the Following JavaScript Object
JSON1

{
"1": {
    "Average": 32.31,
    "Count": 19,
    "Sum": 32.6,
    "Color": "red"
},
"2": {
    "Average": 32.72,
    "Count": 18,
    "Sum": 32.96,
    "Color": "blue"
},
"3": {
    "Average": 31.4,
    "Count": 18,
    "Sum": 31.48,
    "Color": "green"
}
} 

and I want to convert into the following format using javascript ES6 methods.
JSON2

[{
"title": "Average",
"val1": 32.31,
"val2": 32.72,
"val3": 31.4
}, {
"title": "Count",
"val1": 19,
"val2": 18,
"val3": 18
}, {
"title": "Sum",
"val1": 32.6,
"val2": 32.96,
"val3": 31.48
}, {
"title": "Color",
"val1": "red",
"val2": "blue",
"val3": "green"
}]


 Object.keys(json1).forEach((item, index) => {
      let statsList = [];
      Object.keys(json1[item]).forEach(objItem => {
        statsList.push({
          title: objItem,
          val1: boxObj[1][objItem],
          val2: boxObj[2][objItem],
          val3: boxObj[3][objItem]
        });
      });
 console.log(statsList)
    });

var json1 = {
  "1": {
    "Average": 32.31,
    "Count": 19,
    "Sum": 32.6,
    "Color": "red"
  },
  "2": {
    "Average": 32.72,
    "Count": 18,
    "Sum": 32.96,
    "Color": "blue"
  },
  "3": {
    "Average": 31.4,
    "Count": 18,
    "Sum": 31.48,
    "Color": "green"
  }
};

Object.keys(json1).forEach((item, index) => {
  let statsList = [];
  Object.keys(json1[item]).forEach(objItem => {
    statsList.push({
      title: objItem,
      val1: boxObj[1][objItem],
      val2: boxObj[2][objItem],
      val3: boxObj[3][objItem]
    });
  });
  console.log(statsList)
});

Here in the JSON1, the Number of objects can be any number. It has to format it Dynamically. In the JSON2, Instead of val1,val2 anything can be used uniquely identifiable keys in all the objects present in the array. I have tried using forEach, I was able to achieve it with static keys provided, and with the multiple looping statements. I just want with dynamic keys and avoiding multiple loops and I want to know what is the best and easiest way to do this formatting in Javascript. Advance Thanks for your help.

Upvotes: 0

Views: 64

Answers (2)

Yevhen Horbunkov
Yevhen Horbunkov

Reputation: 15540

You may traverse source Object.values() with Array.prototype.reduce() to make up an object that will map each category to all possible values.

Then, you may Array.prototype.map() resulting Object.entries() to return an array of objects with desired structure:

const src = {"1":{"Average":32.31,"Count":19,"Sum":32.6,"Color":"red"},"2":{"Average":32.72,"Count":18,"Sum":32.96,"Color":"blue"},"3":{"Average":31.4,"Count":18,"Sum":31.48,"Color":"green"}},

    resultMap = Object
      .values(src)
      .reduce((r,o,i) => (
        Object
         .entries(o)
         .forEach(([key,value]) => 
          (r[key]=r[key]||{}, r[key][`value${i+1}`] = value))
       ,r),{}),
       
    result = Object
      .entries(resultMap)
      .map(([name,{...values}]) => ({name,...values}))
       
console.log(result)
.as-console-wrapper{min-height:100%;}

If only unique items in each category are required, you may somewhat modify above solution, making use of Set():

const src = {"1":{"Average":32.31,"Count":19,"Sum":32.6,"Color":"red"},"2":{"Average":32.72,"Count":18,"Sum":32.96,"Color":"blue"},"3":{"Average":31.4,"Count":18,"Sum":31.48,"Color":"green"}},

    resultMap = Object
      .values(src)
      .reduce((r,o,i) => (
        Object
         .entries(o)
         .forEach(([key,value]) => 
          (r[key]=r[key]||(new Set()), r[key].add(value)))
       ,r),{}),
       
    result = Object
      .entries(resultMap)
      .map(([name,values]) => ({
        name, 
        ...([...values].reduce((r,v,i) => (r[`value${i+1}`]=v, r),{}))
      }))
       
console.log(result)
.as-console-wrapper{min-height:100%;}

Upvotes: 2

grodzi
grodzi

Reputation: 5703

You may see your data as a matrix: each row is a feature and each column a dimension

  • What you have is the data expressed a rows
  • And what you would like is the data expressed as columns
  • So what you want is the transpose of your matrix

Let's recall that taking the transpose can be done as: T[j][i] = M[i][j] forall i,j

In your case

for each index of row i in M
  for each index of column j in M
    // T[j] is your aggregated record
    // i being the index of the row has to be renamed 'val'+i
    // and you add the property: title: columnOfJ to record T[j]
    T[j]['val' + i] = M[i][j]
    T[j].title = col corresponding to index j

const M = {"1":{"Average":32.31,"Count":19,"Sum":32.6,"Color":"red"},"2":{"Average":32.72,"Count":18,"Sum":32.96,"Color":"blue"},"3":{"Average":31.4,"Count":18,"Sum":31.48,"Color":"green"}}
const T = []
Object.entries(M).forEach(([i, Mi]) => {
  Object.keys(Mi).forEach((col, j) => {
    T[j] = T[j] || {}
    T[j].title = col // we just put the title before so it is the first entry in your record...
    T[j]['val' + i] = Mi[col]
  })
})
console.log(T)

Upvotes: 1

Related Questions