2

Here is my nested object and I need to add "name", "children" for all parent nodes, and "name", "value" for last nodes:

{
    "myObj": {
        "a": {
            "b": {
                "testB1": "10",
                "testB2": "20",
            },
            "c": {
                "testC1": "43",
                "testC2": "",
                "testC3": [
                    {
                        "aa": "34",
                    },
                    {
                        "bb": "43",
                        "cc": "" 
                    },
                ],
                "testC4": {
                    "ee": {
                        "eee1": "11",
                        "eee2": [
                            {
                                "f": "50",
                            },
                            {
                                "i": "70",
                            }
                        ],
                        "eee3": "1"
                    },
                    "ee": "1"
                }
            }
        }
    }
}

Here is what I need (all names and the depth are random):

{name: "myObj", children: [
    {name: "a", children: [
            {name: "b", children:[
                {name: "testB1", value:"10"},
                {name: "testB2": value: "20"} 
            ]} ,
            {name: "c", children: [
                {name: "testC1": value: "43"},
                {name: "testC2": value: ""},
                {name: "testC3", children: [
                    {name: "aa", value: "34"},
                    {name: "bb", value: "43"},
                    {name: "cc", value: ""} 
                ]} ,
                {name: "testC4", children: [
                    {name: "ee", children: [
                        {name: "eee1", value: "11"},
                        {name: "eee2", children: [
                            {name: "f", value: "50"},
                            {name: "i", value: "70"}
                        ]} ,
                        {name: "eee3", value: "1"} 
                    }],
                    {name: "ee", value: "1"} 
                }]
            }]
        }]
    }]
}   

Here is my recursive function:

    function visitDescendants(obj, callback) {
        for (const [key, value] of Object.entries(obj)) {
            if (value && typeof value === "object") {
               let newObj = Object.entries(obj).map( ([key,value]) => ({name: key, children: [value]}) );
                // Recurse
                visitDescendants(value, callback);
            } else {
                let newObj = Object.entries(obj).map( ([key,value]) => ({name: key, value: value}) );
                callback(key, value);
            }
        }    
    }

I can iterate through my object, I can build my desire node, I print out all key values at the last level, but I cannot build the whole object:

visitDescendants(obj, (key, value) => {
    console.log(`${key}: ${value}`);
});
2
  • Your requested output is missing something. You repeat name and value/children in the same object nodes. Do you want different handling for arrays? Commented Mar 4, 2022 at 20:40
  • Have you output that desired result in console? You'd have noticed that information is lost. You'll have to edit your question, as it is unlikely this is what you really want. Commented Mar 4, 2022 at 21:23

1 Answer 1

3

Trying to build such an object during that sort of visitation would require that your callback was stateful, or worse, that you were appending to an out-of-scope (probably global) object, neither of which appeals to me. I would rather simply take apart your object and put it back together as desired, in an immutable manner of course.

Here is an approach which does that. I have to make a guess about how you want arrays handled, as your specified output in that case is not legitimate. A single object cannot have multiple name, value or children properties. But I think this is a reasonable guess, and we can probably alter it easily enough if my guess is wrong.

const transform = (o) =>
  Array .isArray (o)
    ? o .map (transform)
    : Object .entries (o) .map (([k, v]) => ({
        name: k, 
        ... (Object (v) === v ? {children: transform (v)} : {value: v})
      }))

const input = {myObj: {a: {b: {testB1: "10", testB2: "20"}, c: {testC1: "43", testC2: "", testC3: [{aa: "34"}, {bb: "43", cc: ""}], testC4: {ee: {eee1: "11", eee2: [{f: "50"}, {i: "70"}], eee3: "1"}, ee: "1"}}}}}

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

The Object (v) === v is one of many ways to test if something is an object. typeof would work as well, as would some old-school techniques using Object .prototype .toString. I simply prefer this one. The rest of the code should be fairly clear. If we start with an array, we simply map our transformation function over its values. Otherwise, we take its entries as key-value pairs, and for each one build an object that has a name property with that key and either a children property or a value one based upon whether the child is an object or not.

If we could have arrays of scalar values, we might want to expand this slightly to handle non-array/non-object inputs. That version might look like this:

const transform = (o) =>
  Array .isArray (o)
    ? o .map (transform)
  : Object (o) === o
    ? Object .entries (o) .map (([k, v]) => ({
        name: k, 
        ... (Object (v) === v ? {children: transform (v)} : {value: v})
      }))
  : o

const input = {myObj: {foo: [1, 2, 3], a: {b: {testB1: "10", testB2: "20"}, c: {testC1: "43", testC2: "", testC3: [{aa: "34"}, {bb: "43", cc: ""}], testC4: {ee: {eee1: "11", eee2: [{f: "50"}, {i: "70"}], eee3: "1"}, ee: "1"}}}}}
//                     `- note this -'

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

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

1 Comment

Thanks Scott! It works as expected.

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.