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 putting them on to the same level as well as adding 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. And the children level should be indented with 4 white space depending on how deep the level is. The expected output should look like:

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

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

1
  • If you are planning to these data with d3js, you can do this translation easily using d3js, without writing vanilajs. Commented Nov 7, 2019 at 4:16

1 Answer 1

0

Try if this works! Might be a little tricky to explain the code.

I have added a couple of comments at relevant parts of code.

var data = {
  Season1: {
    Title1: {
      a1: {
        val1: "100",
        val2: "150",
        val3: "100"
      },
      a2: {
        val1: "100",
        val2: "200",
        val3: "300"
      }
    },
    Title2: {
      c1: {
        val1: "100",
        val2: "200",
        val3: "300"
      },
      d2: {
        val1: "100",
        val2: "200",
        val3: "300"
      }
    }
  },
  Season2: {
    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"
      }
    }
  }
};

function Format(obj, depth = 0) {
  return Object.entries(obj).flatMap(([key, val]) => {
    if (
      Object.keys(val).some(function(k) {
        return typeof val[k] === "object";
      })
    ) {
      // Pad keys with spaces based on depth
      let o = {
        name: key.padStart(depth * 4 + key.length)
      };
      const children = Format(val, depth + 1);
      // Get the sum of children that are only only one level deep 
      const childrenSum = children
        .filter(
          ({
            name
          }) =>
          name.length - name.replace(/\s/g, "").length === (depth + 1) * 4
        )
        // Filter out the name key as they are not numbers 
        .reduce((acc, temp) => {
          Object.entries(temp)
            .filter(([key, val]) => key !== "name")
            .forEach(([key, val]) => {
              acc[key] = (acc[key] || 0) + Number(val);
            });
          return acc;
        }, {});
      o = { ...o,
        ...childrenSum
      };
      return [o, ...children];
    } else {
      let o = {
        name: key.padStart(depth * 4 + key.length)
      };
      Object.keys(val).map(function(a) {
        o[a] = val[a];
      });
      return [o];
    }
  });
}

console.log(Format(data));

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

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.