Home > database >  Javascript group nested object array into another array
Javascript group nested object array into another array

Time:11-25

I have an array that is used to track status of certain documents. This looks like the following when outputted from a Mongo Aggregate function. k is the status, and v is the count of documents with this status.

[
    {
        "legal": [
            { "k": "", "v": 6 },
            { "v": 3},
            { "k": "To be uploaded", "v": 5 },
            { "k": "To be reviewed", "v": 96 }
        ],
        "operations": [
            { "v": 1 },
            { "k": "To be uploaded", "v": 3 },
            { "k": "To be reviewed", "v": 24 }
        ],
        "accounting": [
            { "k": "To be reviewed", "v": 137 },
            { "k": "", "v": 3 },
            { "v": 2 },
            { "k": "To be uploaded", "v": 24 },
            { "k": "Not Required", "v": 1 }
        ]
    }
]

I want to graph these on a stacked bar chart, and require the following format. The data array is in the order ["legal", "operations", "accounting"]

 [
    {
      name: "",  // no status specified
      data: [9, 1, 5]
    },
    {
      name: "To be uploaded",
      data: [5, 3, 24]
    },
    {
      name: "To be reviewed",
      data: [96, 24, 137]
    },
    {
      name: "Not Required",
      data: [0, 0, 1]
    },
  ]

CodePudding user response:

One approach would be to use a combination of Array#map and Array#reduce methods as follows. Array#reduce would be great at creating a summary of and cleaning up the data object, while Array#map would help with the final presentation. It can be improved upon, of course:

const input = [ { "legal": [ { "k": "", "v": 6 }, { "v": 3}, { "k": "To be uploaded", "v": 5 }, { "k": "To be reviewed", "v": 96 } ], "operations": [ { "v": 1 }, { "k": "To be uploaded", "v": 3 }, { "k": "To be reviewed", "v": 24 } ], "accounting": [ { "k": "To be reviewed", "v": 137 }, { "k": "", "v": 3 }, { "v": 2 }, { "k": "To be uploaded", "v": 24 }, { "k": "Not Required", "v": 1 } ] } ],

    summary = input.map(
      ({legal,operations,accounting}) =>
      ({
          legal:legal.reduce((acc,{k,v}) => ({...acc,[k || "NONE"]:(acc[k || "NONE"] || 0)   v}),{"Not Required":0}),
          operations:operations.reduce((acc,{k,v}) => ({...acc,[k || "NONE"]:(acc[k || "NONE"] || 0)   v}),{"Not Required":0}),
          accounting:accounting.reduce((acc,{k,v}) => ({...acc,[k || "NONE"]:(acc[k || "NONE"] || 0)   v}),{"Not Required":0})
      })
    ),
    
    output = summary
    .flatMap(
      o => Object.keys(Object.values(o)[0])
      .map(
        z => ({
          name: z === "NONE" ? "" : z,
          data: Object.keys(o).map(y => o[y][z])
        }) 
      ) 
    );
    
console.log( output );

Alternatively .....

You can create summary dynamically as follows:

const input = [ { "legal": [ { "k": "", "v": 6 }, { "v": 3}, { "k": "To be uploaded", "v": 5 }, { "k": "To be reviewed", "v": 96 } ], "operations": [ { "v": 1 }, { "k": "To be uploaded", "v": 3 }, { "k": "To be reviewed", "v": 24 } ], "accounting": [ { "k": "To be reviewed", "v": 137 }, { "k": "", "v": 3 }, { "v": 2 }, { "k": "To be uploaded", "v": 24 }, { "k": "Not Required", "v": 1 } ] } ],

    summary = input.flatMap(
      o =>
      Object.keys(o)
      .reduce(
        (ac,cat) => 
        ({
          ...ac,
          [cat]:o[cat].reduce((acc,{k,v}) => ({...acc,[k || "NONE"]:(acc[k || "NONE"] || 0)   v}),{"Not Required":0})
        }),{}
      )
    ),
    
    output = summary
    .flatMap(
      o => Object.keys(Object.values(o)[0])
      .map(
        z => ({
          name: z === "NONE" ? "" : z,
          data: Object.keys(o).map(y => o[y][z])
        }) 
      )
    );
    
console.log( output );

  • Related