skip to Main Content

I am having array of items with following order:

const arr = [
  {
    group: { id: "group1", groupname: "groupname1" },
    categories: [
      {
        id: "cat1",
        categoryName: "category1",
        total: 5,
        available: 2,
        subCategories: []
      },
      {
        id: "cat2",
        categoryName: "category2",
        total: 15,
        available: 12,
        subCategories: [
          {
            id: "cat3",
            categoryName: "category3",
            total: 15,
            available: 12,
            subCategories: []
          }
        ]
      }
    ]
  },
  {
    group: { id: "group2", groupname: "groupname2" },
    categories: [
      {
        id: "cat4",
        categoryName: "category4",
        total: 25,
        available: 22,
        subCategories: []
      },
      {
        id: "cat5",
        categoryName: "category5",
        total: 50,
        available: 25,
        subCategories: []
      }
    ]
  }
];

This needs to be converted into array of recursive items, witj key, name, and children properties. The result would be like this,

[
  {
    "key": "group1",
    "name": "groupname1",
    "total": 20,
    "available": 24,
    "children": [
      {
        "key": "cat1",
        "name": "category1",
        "total": 5,
        "available": 2,
        "children": []
      },
      {
        "key": "cat2",
        "name": "category2",
        "total": 15,
        "available": 12,
        "children": [
          {
            "key": "cat3",
            "name": "category3",
            "total": 15,
            "available": 12,
            "children": []
          }
        ]
      }
    ]
  },
  {
    "key": "group2",
    "name": "groupname2",
    "total": 75,
    "available": 47,
    "children": [
      {
        "key": "cat4",
        "name": "category4",
        "total": 25,
        "available": 22,
        "children": []
      },
      {
        "key": "cat5",
        "name": "category5",
        "total": 50,
        "available": 25,
        "children": []
      }
    ]
  }
]

I am able to bring down to the above structure using the following snippet.

const formatter = (data) => {
  const recursiveTree = (item) => {
    if (item.group) {
      const {
        group: { id, groupname, total, available },
        categories
      } = item;
      return {
        key: id,
        name: groupname,
        total: total || 0,
        available: available || 0,
        children: categories?.map(recursiveTree)
      };
    }
    const { id, categoryName, total, available, subCategories } = item;
    return {
      key: id,
      name: categoryName,
      total: total || 0,
      available: available || 0,
      children: subCategories.map(recursiveTree)
    };
  };
  return data.map(recursiveTree);
};

I am stuck at deriving total and available of the root note. This node should derive its total and available from its children values. Can someone help me how to do this recursively? This could be n level too

2

Answers


  1. You could add some post-processing. Replace this:

      return data.map(recursiveTree);
    

    with:

      const result = data.map(recursiveTree);
      for (const item of result) {
          if (!item.children) continue;
          item.total = item.children.reduce((total, child) => total + child.total, 0);
          item.available = item.children.reduce((total, child) => total + child.available, 0);
      }
      return result;
    
    Login or Signup to reply.
  2. One approach separates out the reformatting of your input structure and the totaling of properties. Here we have an non-mutating version that works like that:

    const restructure = (xs) => xs .map (x => ({
      key: x ?.group ?.id ?? x .id,
      name: x ?.group ?.groupname ?? x .categoryName,
      total: x .total ?? 0,
      available: x .available ?? 0,
      children: restructure (x .categories ?? x .subCategories)
    }))
    
    const sumOn = (field) => ({[field]: fld, children = [], ...rest}, _, __, kids = children .map (sumOn (field))) => ({
      ...rest,
      [field]: fld + kids .reduce ((t, kid) => t + kid [field], 0),
      children: kids
    })
    
    const process = (xs) => 
      restructure (arr) .map (sumOn ('total')) .map (sumOn ('available'))
    
    const arr = [{group: {id: "group1", groupname: "groupname1"}, categories: [{id: "cat1", categoryName: "category1", total: 5, available: 2, subCategories: []}, {id: "cat2", categoryName: "category2", total: 10, available: 5, subCategories: [{id: "cat3", categoryName: "category3", total: 15, available: 12, subCategories: []}]}]}, {group: {id: "group2", groupname: "groupname2"}, categories: [{id: "cat4", categoryName: "category4", total: 25, available: 22, subCategories: []}, {id: "cat5", categoryName: "category5", total: 50, available: 25, subCategories: []}]}]
    
    
    console .log (process (arr))
    .as-console-wrapper {max-height: 100% !important; top: 0}

    Here restructure converts your slightly odd input into a consistent recursive structure. sumOn takes a field name and returns a function which totals values of that field by recursively adding those of each of its children. (This would be slightly cleaner with a sum helper; but we can leave that as an exercise.)

    Our main function, process, simply calls restructure with the input, and then maps sumOn over the results twice, once with 'total' and once with 'available'.

    I find this a very clean way to break down the problem into reusable chunks and simple steps.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search