0

Have the following tree json object:

{
  "Season1": {
    "Title1": {
      "a1": {
        "val1": "100",
        "val2": "200",
        "val3": "300"
      },
      "a2": {
        "val1": "100",
        "val2": "200",
        "val3": "300"
      }
    },
    "Title2": {
      "c1": {
        "val1": "100",
        "val2": "200",
        "val3": "300"
      },
      "d2": {
        "val1": "100",
        "val2": "200",
        "val3": "300"
      }
    }
  }
}

Tried to format the json using the following function:

function Format(obj){
    return Object.entries(obj).flatMap(([key, val]) => {
        let o = { name: key}
        if(Object.keys(val).some(function(k) {return typeof val[k] === 'object'})){
            o['_children'] = Format(val)
        } else {
            Object.keys(val).map(function(a){
                o[a] = val[a]
            })
        }
        return [o]
    })
}

That will return an array of nested objects by keys:

[
  {
    "name": "Season1",
    "_children": [
      {
        "name": "Title1",
        "_children": [
          {
            "name": "a1",
            "val1": "100",
            "val2": "200",
            "val3": "300"
          },
          {
            "name": "a2",
            "val1": "100",
            "val2": "200",
            "val3": "300"
          }
        ]
      },
      {
        "name": "Title2",
        "_children": [
          {
            "name": "c1",
            "val1": "100",
            "val2": "200",
            "val3": "300"
          },
          {
            "name": "d2",
            "val1": "100",
            "val2": "200",
            "val3": "300"
          }
        ]
      }
    ]
  }
]

The challenge is to calculate the subtotal of the bottom level keys, which are val1, val2, val3, in each parent levels recursively, .e.g. "Title1", "Title2", and "Season1", therefore after populating the output into a table can fill up the blank subtotal cells. The expected output should look like:

[
  {
    "name": "Season1",
    "_children": [
      {
        "name": "Title1",
        "_children": [
          {
            "name": "a1",
            "val1": "100",
            "val2": "200",
            "val3": "300",
          },
          {
            "name": "a2",
            "val1": "100",
            "val2": "200",
            "val3": "300",
          }
        ],
        "val1": 200,
        "val2": 400,
        "val3": 600
      },
      {
        "name": "Title2",
        "_children": [
          {
            "name": "c1",
            "val1": "100",
            "val2": "200",
            "val3": "300",
          },
          {
            "name": "d2",
            "val1": "100",
            "val2": "200",
            "val3": "300",
          }
        ],
        "val1": 400,
        "val2": 400,
        "val3": 600
      }
    ],
    "val1": 600,
    "val2": 800,
    "val3": 1200
  }
]

How to update the Format function for that purpose? Can someone share any thoughts or solution? Thanks!

2 Answers 2

1

You can also edit your recursive function like below. I'm accumulating the sum at each level of recursion and passing it up to the call stack

data = {
  Season1: {
    Title1: {
      a1: {
        val1: "100",
        val2: "200",
        val3: "300"
      },
      a2: {
        val1: "100",
        val2: "200",
        val3: "300"
      }
    },
    Title2: {
      c1: {
        val1: "100",
        val2: "200",
        val3: "300"
      },
      d2: {
        val1: "300",
        val2: "200",
        val3: "300"
      }
    }
  }
};


function merge(srcObj, destObj){
  Object.keys(srcObj).forEach(key => {
    if(destObj[key]) {
      destObj[key] = destObj[key] + srcObj[key]
    }
    else{
      destObj[key] = srcObj[key];
    }
  });
  return destObj;
}



function Format(obj, valObj = {}, level = 0) { 
    valObj[level] = {};
    return Object.entries(obj).flatMap(([key, val]) => {
      let o = { name: key };
      if (typeof val === "object") {
        o["_children"] = Format(val, valObj, level + 1);
        valObj[level] = merge(valObj[level+1],valObj[level] || {});
        if(level <2){
          o = { ...o, ...valObj[level + 1]};
        }       
      } else {     
        valObj[level][key] =  Number.parseInt(val);
        o["value"] = val;
      }
      return [o];
    });
}

a =  Format(data);




console.log(a);

For your modified format function, you can try like below.

data = {
  Season1: {
    Title1: {
      a1: {
        val1: "100",
        val2: "200",
        val3: "300"
      },
      a2: {
        val1: "100",
        val2: "200",
        val3: "300"
      }
    },
    Title2: {
      c1: {
        val1: "100",
        val2: "200",
        val3: "300"
      },
      d2: {
        val1: "300",
        val2: "200",
        val3: "300"
      }
    }
  }
};

function merge(srcObj, destObj){
  Object.keys(srcObj).forEach(key => {
    if(destObj[key]) {
      destObj[key] = destObj[key] + srcObj[key]
    }
    else{
      destObj[key] = srcObj[key];
    }
  });
  return destObj;
}


function Format(obj, valObj = {}, level=0){
    valObj[level] = {};
    return Object.entries(obj).flatMap(([key, val]) => {
        let o = { name: key}
        if(Object.keys(val).some(function(k) {return typeof val[k] === 'object'})){
            o['_children'] = Format(val, valObj, level + 1);
          valObj[level] = merge(valObj[level+1],valObj[level] || {});
            if(level <2){
              o = { ...o, ...valObj[level + 1]};
            }
        } else {
            Object.keys(val).map(function(a){
                valObj[level][a] = (valObj[level][a] || 0) + Number.parseInt(val[a]);
                o[a] = val[a]
            })
        }
        return [o];
    })
}


a =  Format(data);
console.log(a);

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

3 Comments

thanks @nithin, the solution was effective. Later I update the function Format() in the question to make the bottom level neater, which removes the _children object at the bottom and directly put the keys value pairs with the names. I tried to modify your solution but couldn't get it work after the Format function changed, can you help advise how?
@ctlkkc No problem. I have tweaked the code to work with the new Format function. Please check the updated answer. Please mark right answer / upvote if this was helpful :). Good day :)
Thanks. @nithin can you also help in a similar question? thank you. stackoverflow.com/questions/58741717/…
0

You could update all levels, except the most nested one.

function sum(array, sums = {}) {
    var update;
    array.forEach(o => {
        if (!o._children) {
            sums[o.name] = (sums[o.name] || 0) + +o.value;
            return;
        }
        var x = {}
        sum(o._children, x) && Object.assign(o, x);
        Object.keys(x).forEach(k => sums[k] = (sums[k] || 0) + x[k]);
        update = true;
    });
    return update;
}

var data = [{ name: "Season1", _children: [{ name: "Title1", _children: [{ name: "a1", _children: [{ name: "val1", value: "100" }, { name: "val2", value: "200" }, { name: "val3", value: "300" }] }, { name: "a2", _children: [{ name: "val1", value: "150" }, { name: "val2", value: "260" }, { name: "val3", value: "370" }] }] }, { name: "Title2", _children: [{ name: "c1", _children: [{ name: "val1", value: "110" }, { name: "val2", value: "220" }, { name: "val3", value: "330" }] }, { name: "d2", _children: [{ name: "val1", value: "101" }, { name: "val2", value: "202" }, { name: "val3", value: "303" }] }] }] }];

sum(data);

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

Comments

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.