2

I am trying to work with a json data which needs to be changed in many ways.

My current json data is following:

{
  "file1": {
    "function1": {
      "calls": {
        "105:4": {
          "file": "file2",
          "function": "function5"
        },
        "106:4": {
          "file": "file2",
          "function": "function6"
        }
      },
      "lines1": {
        "123": "102:0",
        "456": "105:8"
      },
      "lines2": {
        "102:0": [
          "102:0"
        ],
        "105:4": [
          "106:4",
          "107:1"
        ],
        "106:4": [
          "107:1"
        ]
      }
    }
  }
}

But I want the data as following:

{
  "name": "program",
  "children": [
    {
      "name": "file1",
      "children": [
        {
          "name": "function1",
          "calls": [
            {
              "line": 105,
              "file": "file2",
              "function": "function5"
            },
            {
              "line": 106,
              "file": "file2",
              "function": "function6"
            }
          ],
          "lines1": [
            102,
            105
          ],
          "lines2": [
            [
              102,
              102
            ],
            [
              105,
              106,
              107
            ],
            [
              106,
              107
            ]
          ],
          "group": 1
        }
      ],
      "group": 1
    }
  ],
  "group": 0
}

Here, number of files and functions are more. The value of first name is user defined. The group information is depend on the parent-child. Each file will have a group ascending group number and all the functions inside the file will also have the same group number. For the values for lines the first part before : are taken (104:4 becomes 104).

I have tried with following code so far, which is incomplete and not handling group information correctly.

function build(data) {
    return Object.entries(data).reduce((r, [key, value], idx) => {
      const obj = {
        name: 'program',
        children: [],
        group: 0,
        lines: []
      }

      if (key !== 'lines2) {
        obj.name = key;
        obj.children = build(value)
          if(!(key.includes(":")))
          obj.group = idx + 1;
      } else {
        if (!obj.lines) obj.lines = [];
        Object.entries(value).forEach(([k, v]) => {
          obj.lines.push([k, ...v].map(e => e.split(':').shift()))
        })
      }

      r.push(obj)
      return r;
    }, [])
  }

  const result = build(data);
  console.log(result);

I would really appreciate if you can help me out. Thanks in advance for your time.

1 Answer 1

2

Assuming the structure of your input is consistenly defined as shown in your question (ie, that no "saftey checks" are needed, etc), then you could approach this using a combination of Object.entries(), Array.map() and spread syntax as shown below.

See inline documentation in this code snippet for details on how to achieve that:

function transformData(data, programName) {

  /* Define local parse helper to extract number from NUMBER:STRING format */    
  const parseHelper = (str) => Number.parseInt(str.split(':')[0]);
      
  /* Define local parse helper to extract group number from STRINGNUMBER 
  format */    
  const parseGroup = (str) => Number.parseInt(str.replace(/^[a-z]+/,""))
      
  /* Create a root object with specified program name */
  return {
    name : programName,
    
    /* Iterate each file name and object entry of input */
    children : Object.entries(input).map(([fileName, fileObject]) => {

      /* Iterate function name and object of current file object */
      const fileChildren = Object.entries(fileObject)
        .map(([functionName, functionObject]) => {

        /* Iterate function name and object of current file object */
        const lines = Object.entries(functionObject)
          .reduce((target, [functionKey, functionValue]) => {

            if(functionKey === "calls") {

              /* If function key is calls, interpret this value as data to be
              transformed to desired calls object shape */
              const calls = Object.entries(functionValue)
                .map(([callKey, callObject]) => {

                return {
                  line : parseHelper(callKey),
                  file : callObject['file'],
                  function : callObject['function']
                }
              });
              
              /* Inject calls object into lines result */
              return {
                ...target,
                calls
              };
            }
            else {

              /* Otherwise, interpret this value as data to be transformed to 
                 desired lines object shape */
              const lineValues = Object.entries(functionValue)
                .map(([key, value]) => {

                /* If value is an array, map key/value pair to a nested array
                   in resulting linesValues array */
                return Array.isArray(value) ? [key, ...value]
                 .map(parseHelper) : parseHelper(value)
              })

              /* Inject line values into function key of result */
              return {
                ...target,
                [functionKey] : lineValues
              }
            }

        }, {});
        
        /* Inject lines into function result */
        return {
          name : functionName,
          ...lines,
          group : parseGroup(functionName)
        }
      });

      /* Map file object to name/children pairing */
      return { 
        name : fileName,
        children : fileChildren,
          group : parseGroup(fileName)
      }
    }),
    
    group : 0
  }
}

const input = {
  "file1": {
    "function1": {
      "calls": {
        "105:4": {
          "file": "file2",
          "function": "function5"
        },
        "106:4": {
          "file": "file2",
          "function": "function6"
        }
      },
      "lines1": {
        "123": "102:0",
        "456": "105:8"
      },
      "lines2": {
        "102:0": [
          "102:0"
        ],
        "105:4": [
          "106:4",
          "107:1"
        ],
        "106:4": [
          "107:1"
        ]
      }
    }
  }
};

console.log(transformData(input, "program"))

Hope that helps!

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

8 Comments

Thank you so much !! But group information is not there :(
You're welcome - sorry to miss that, just updated the answer. Does that help?
I am so sorry. The json output is correct but this new structure is not working with my existing d3 code. Its showing following error: Argument of type '{ name: string; children: { group: number; name: string; }[]; group: number; }[]' is not assignable to parameter of type 'readonly string[]'. Type '{ name: string; children: { group: number; name: string; }[]; group: number; }' is not assignable to type 'string'. Can you please help me out? :(
I just wanted to access the children from the data which is the output of your code by const colorScale = d3.scaleOrdinal() .domain(data.children) .range(d3.schemeCategory10);
Are you able to post a new question with the D3 code and send me a link to that?
|

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.