0

I have one array that contains hierarchical data, i wan to convert it to hierarchy but in different format. I tried to use recursive method but its not giving proper data

Below is my code what i tried to implement

static convertConfigJson(data, finalResults = []) {

        const configData = JSON.parse(JSON.stringify(data));

        return configData.map((item) => {
            const { name, type, value, path } = item;
            let children = {}
            if (type === 'Object') {
              children = this.convertConfigJson(value, finalResults);
            } else {

            finalResults.push({
                id: name,
                name: name,
                children: children
            })
            }




            console.log('>>>>>>>>>', finalResults)

            return finalResults;
          });   
    }

Below is JSON array that i am getting from API

[
        {
            "name": "info",
            "type": "Object",
            "value": [
                {
                    "name": "app",
                    "type": "Object",
                    "value": [
                        {
                            "path": "info.app",
                            "name": "encoding",
                            "type": "TEXT",
                            "value": "@project.build.sourceEncoding@"
                        },
                        {
                            "name": "java",
                            "type": "Object",
                            "value": [
                                {
                                    "path": "info.app.java",
                                    "name": "source",
                                    "type": "TEXT",
                                    "value": "@java.version@"
                                },
                                {
                                    "path": "info.app.java",
                                    "name": "target",
                                    "type": "TEXT",
                                    "value": "java.version@"
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]

I want to convert above array in below format i.e. change value array to children

[
    {
        "name": "info",
        "type": "Object",
        "children": [
            {
                "name": "app",
                "type": "Object",
                "children": [
                    {
                        "path": "info.app",
                        "name": "encoding",
                        "type": "TEXT",
                        "value": "@project.build.sourceEncoding@"
                    },
                    {
                        "name": "java",
                        "type": "Object",
                        "children": [
                            {
                                "path": "info.app.java",
                                "name": "source",
                                "type": "TEXT",
                                "value": "@java.version@"
                            },
                            {
                                "path": "info.app.java",
                                "name": "target",
                                "type": "TEXT",
                                "value": "java.version@"
                            }
                        ]
                    }
                ]
            }
        ]
    }
]

4 Answers 4

5

Simple recursive walk on all elements in children property. set .value to .children and delete .value

walk = node => {
  if(!node.value || typeof node.value!=='object') return
  node.children = node.value
  delete node.value
  node.children.forEach(walk)
}

data.forEach(walk)

console.log(data)
<script>
data=[
        {
            "name": "info",
            "type": "Object",
            "value": [
                {
                    "name": "app",
                    "type": "Object",
                    "value": [
                        {
                            "path": "info.app",
                            "name": "encoding",
                            "type": "TEXT",
                            "value": "@project.build.sourceEncoding@"
                        },
                        {
                            "name": "java",
                            "type": "Object",
                            "value": [
                                {
                                    "path": "info.app.java",
                                    "name": "source",
                                    "type": "TEXT",
                                    "value": "@java.version@"
                                },
                                {
                                    "path": "info.app.java",
                                    "name": "target",
                                    "type": "TEXT",
                                    "value": "java.version@"
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
</script>

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

6 Comments

@Disfigure, what will be gained on 10 000 elements?
@Disfigure: how much performance boost do you think is worth the extra code complexity? This is a nice simple solution.
@user120242 I agree, you said "mostly negligible" so the difference exists :)
right, but as you can see in the article on V8 as an example, forEach has native level optimizations. In some cases, depending on what you are doing, it can actually be faster
|
1

write transform method, When ever object has the "value" type is Array then call the transform recursively.

const transform = ({ value, ...rest }) => {
  const item = { ...rest };
  Array.isArray(value)
    ? (item["children"] = value.map(transform))
    : (item["value"] = value);
  return item;
};

// Update: Based on @user120242 suggestion, creating another object is not required and using `Object.assign` will simplify.

const transform2 = ({ value, ...rest }) =>
  Object.assign(
    rest,
    Array.isArray(value) ? { children: value.map(transform2) } : { value }
  );

const items = [
  {
    name: "info",
    type: "Object",
    value: [
      {
        name: "app",
        type: "Object",
        value: [
          {
            path: "info.app",
            name: "encoding",
            type: "TEXT",
            value: "@project.build.sourceEncoding@",
          },
          {
            name: "java",
            type: "Object",
            value: [
              {
                path: "info.app.java",
                name: "source",
                type: "TEXT",
                value: "@java.version@",
              },
              {
                path: "info.app.java",
                name: "target",
                type: "TEXT",
                value: "java.version@",
              },
            ],
          },
        ],
      },
    ],
  },
];

const output = items.map(transform);
console.log(output);

console.log(items.map(transform2));

3 Comments

@ScottSauyet, Yeah, Did not see your answer before posting. even came up with same method name transform :)
@user120242, Agree, creating new clone is unnecessary. should have updated rest itself. Just updated answer with alternate way using the Object.assign. Thank you.
That updated version is very nice! I think I'll add an update to mine to match.
1

This technique is similar to the answer from user120242, but it does not mutate your input structure, creating a new one instead:

const transform = ({value, ...rest}) => 
  Array .isArray (value)
    ? {...rest, children: value .map (transform)}
    : {...rest, value}

const data = [{name: "info", type: "Object", value: [{name: "app", type: "Object", value: [{path: "info.app", name: "encoding", type: "TEXT", value: "@project.build.sourceEncoding@"}, {name: "java", type: "Object", value: [{path: "info.app.java", name: "source", type: "TEXT", value: "@java.version@"}, {path: "info.app.java", name: "target", type: "TEXT", value: "java.version@"}]}]}]}]

console .log (data .map (transform))

Update

Borrowing a technique from sivako, we can simplify this nicely:

const transform = ({value, ...rest}) => ({
  ... rest, 
  ... (Array .isArray (value) ? {children: value .map (transform)} : {value})
})

const data = [{name: "info", type: "Object", value: [{name: "app", type: "Object", value: [{path: "info.app", name: "encoding", type: "TEXT", value: "@project.build.sourceEncoding@"}, {name: "java", type: "Object", value: [{path: "info.app.java", name: "source", type: "TEXT", value: "@java.version@"}, {path: "info.app.java", name: "target", type: "TEXT", value: "java.version@"}]}]}]}]

console .log (data .map (transform))

Comments

0

An easiest way is just replace all "value": [ to "children": [ after stringify

function convertConfigJson(data) {
  const configData = JSON.parse(JSON.stringify(data).replace(/"value":\[/g, '"children":['));
  return configData
}

also you can go one by one through all keys of nested object, and replace value keys with array in value to children

3 Comments

This solution has a drawback, that it will also replace words "value" if they are in value of any key of this object.
@matvs, no, because square brackets are included...for ts structure it is okay but as universal solution it should not be used as this, i just suggested the most simple way in sense of code. But solution with loop over all elements is more reasonable of course
Thanks @DmitryReutov, i need a way with recursive method call, i wight need to update other properties or object structure

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.