REDDY PRASAD
REDDY PRASAD

Reputation: 1409

Better way to create nested array with count equals to children's total count

I have input data structure like

const i = [
  { Time: "Breakfast", City: "Bangalore", Area: "Bellandur", count: 12 },
  { Time: "Breakfast", City: "Hyderabad", Area: "LBNagar", count: 19 },
  { Time: "Lunch", City: "Bangalore", Area: "Koramangala", count: 22 },
  { Time: "Dinner", City: "Hyderabad", Area: "Koti", count: 10 },
  { Time: "Dinner", City: "Bangalore", Area: "Bellandur", count: 16 },
  { Time: "Lunch", City: "Hyderabad", Area: "LBNagar", count: 28 },
  { Time: "Breakfast", City: "Bangalore", Area: "Bellandur", count: 11 },
  { Time: "Lunch", City: "Hyderabad", Area: "Koti", count: 24 },
  { Time: "Dinner", City: "Bangalore", Area: "Koramangala", count: 27 },
  { Time: "Breakfast", City: "Bangalore", Area: "Bellandur", count: 22 },
  { Time: "Breakfast", City: "Hyderabad", Area: "Manikonda", count: 11 },
  { Time: "Dinner", City: "Hyderabad", Area: "Manikonda", count: 10 },
  { Time: "Lunch", City: "Bangalore", Area: "Bellandur", count: 17 }
];

Expected transformed data structure should be nested array with count prop equals to sum of children's count value

[
  {
    "d": "Breakfast",
    "count": 42,
    "children": [
      {
        "d": "Bangalore",
        "count": 12,
        "children": [
          {
            "d": "Bellandur",
            "count": 12,
            "children": []
          }
        ]
      },
      {
        "d": "Hyderabad",
        "count": 30,
        "children": [
          {
            "d": "LBNagar",
            "count": 19,
            "children": []
          },
          {
            "d": "Manikonda",
            "count": 11,
            "children": []
          }
        ]
      }
    ]
  },
  {
    "d": "Lunch",
    "count": 91,
    "children": [
      {
        "d": "Bangalore",
        "count": 39,
        "children": [
          {
            "d": "Koramangala",
            "count": 22,
            "children": []
          },
          {
            "d": "Bellandur",
            "count": 17,
            "children": []
          }
        ]
      },
      {
        "d": "Hyderabad",
        "count": 52,
        "children": [
          {
            "d": "LBNagar",
            "count": 28,
            "children": []
          },
          {
            "d": "Koti",
            "count": 24,
            "children": []
          }
        ]
      }
    ]
  },
  {
    "d": "Dinner",
    "count": 63,
    "children": [
      {
        "d": "Hyderabad",
        "count": 20,
        "children": [
          {
            "d": "Koti",
            "count": 10,
            "children": []
          },
          {
            "d": "Manikonda",
            "count": 10,
            "children": []
          }
        ]
      },
      {
        "d": "Bangalore",
        "count": 43,
        "children": [
          {
            "d": "Bellandur",
            "count": 16,
            "children": []
          },
          {
            "d": "Koramangala",
            "count": 27,
            "children": []
          }
        ]
      }
    ]
  }
]

To achieve above result, I have snippet

const input = [
  { Time: "Breakfast", City: "Bangalore", Area: "Bellandur", count: 12 },
  { Time: "Breakfast", City: "Hyderabad", Area: "LBNagar", count: 19 },
  { Time: "Lunch", City: "Bangalore", Area: "Koramangala", count: 22 },
  { Time: "Dinner", City: "Hyderabad", Area: "Koti", count: 10 },
  { Time: "Dinner", City: "Bangalore", Area: "Bellandur", count: 16 },
  { Time: "Lunch", City: "Hyderabad", Area: "LBNagar", count: 28 },
  { Time: "Breakfast", City: "Bangalore", Area: "Bellandur", count: 11 },
  { Time: "Lunch", City: "Hyderabad", Area: "Koti", count: 24 },
  { Time: "Dinner", City: "Bangalore", Area: "Koramangala", count: 27 },
  { Time: "Breakfast", City: "Bangalore", Area: "Bellandur", count: 22 },
  { Time: "Breakfast", City: "Hyderabad", Area: "Manikonda", count: 11 },
  { Time: "Dinner", City: "Hyderabad", Area: "Manikonda", count: 10 },
  { Time: "Lunch", City: "Bangalore", Area: "Bellandur", count: 17 }
];

// convert nested array into map
// Ex: {Breakfast: { Bangalore: {Bellandur: 40 }, ... } ... }}}
const nestedMap = input.reduce((acc, v) => {
  if (!acc[v["Time"]]) {
    acc[v["Time"]] = {};
  }

  if (!acc[v["Time"]][v["City"]]) {
    acc[v["Time"]][v["City"]] = {};
  }

  if (!acc[v["Time"]][v["City"]][v["Area"]]) {
    acc[v["Time"]][v["City"]][v["Area"]] = v["count"];
  }

  return acc;
}, {});

const summer = (o, i) =>
  typeof o === "number"
    ? i + o
    : Object.values(o).reduce((acc, v) => acc + summer(v, i), 0);

const aggregator = o => {
  return typeof o === "number"
    ? []
    : Object.entries(o).map(([n, child]) => ({
        d: n,
        count: summer(child, 0),
        children: aggregator(child)
      }));
};

const result = aggregator(nestedMap);

console.log(JSON.stringify(result, undefined, 2));

Thought my snippet working perfectly, It involves lot of iterations. First converting into map and one iteration (summer function) to get count of children and other iteration (aggregator function) to form nested array.

I am looking for better performant solution. Thanks in advance!

Upvotes: 1

Views: 96

Answers (1)

Nina Scholz
Nina Scholz

Reputation: 386680

You could take a more concise approach with an array of the nested keys and seach for the object with the value at the level.

Then add count and return the actual object.

This approach prevents tailing children arrays

var data = [{ Time: "Breakfast", City: "Bangalore", Area: "Bellandur", count: 12 }, { Time: "Breakfast", City: "Hyderabad", Area: "LBNagar", count: 19 }, { Time: "Lunch", City: "Bangalore", Area: "Koramangala", count: 22 }, { Time: "Dinner", City: "Hyderabad", Area: "Koti", count: 10 }, { Time: "Dinner", City: "Bangalore", Area: "Bellandur", count: 16 }, { Time: "Lunch", City: "Hyderabad", Area: "LBNagar", count: 28 }, { Time: "Breakfast", City: "Bangalore", Area: "Bellandur", count: 11 }, { Time: "Lunch", City: "Hyderabad", Area: "Koti", count: 24 }, { Time: "Dinner", City: "Bangalore", Area: "Koramangala", count: 27 }, { Time: "Breakfast", City: "Bangalore", Area: "Bellandur", count: 22 }, { Time: "Breakfast", City: "Hyderabad", Area: "Manikonda", count: 11 }, { Time: "Dinner", City: "Hyderabad", Area: "Manikonda", count: 10 }, { Time: "Lunch", City: "Bangalore", Area: "Bellandur", count: 17 }],
    keys = ['Time', 'City', 'Area'],
    result = data
        .reduce((r, o) => {
            keys.reduce((p, k) => {
                var temp = (p.children = p.children || []).find(q => q.d === o[k]);
                if (!temp) p.children.push(temp = { d: o[k], count: 0 });
                temp.count += o.count;
                return temp;
            }, r);
            return r;
        }, { children: [] })
        .children;

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

Related Questions