0

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]
    },
  ]

1 Answer 1

1

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

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks... great insight into breaking it down. However, what if the categories (e.g. Legal etc...) are dynamic. If a new category is introduced, then will that need to be mapped manually within the Array#map method?
That should not be a problem at all as Object#keys can be used with Array#reduce to dynamically create summary.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.