1

I've got an object that looks like this:

{
    parent: {
        child1: {
            key: 'value'
        },
        child2: {
            key: 'value'
        },
        child3: {
            key: 'value'
        }
    }
}

I need to transform it to an object that looks like this:

{
    title: 'parent',
    children: [{
        title: 'child1',
        children: [{
            title: 'key',
            value: 'value'
         }]
     }, {
        title: 'child2',
        children: [{
            title: 'key',
            value: 'value'
         }]
     }, {
        title: 'child3',
        children: [{
            title: 'key',
            value: 'value'
         }]
    }]
}

I ended up with a following function:

function transform(obj) {
    const result = {
        title: '',
        children: []
    };
    for (let key in obj) {
        let child = obj[key];
        result.title = key;
        if (typeof(child) === 'string') {
            delete result.children;
            result.value = child;
        } else {
            result.children.push(transform(child));
        }
    }
    return result;
}

But when I run it, it returns me the following output, which is wrong:

{
    title: 'parent',
    children: [{
        title: 'child3',
        children: [
            { title: 'key', value: 'value' },
            { title: 'key', value: 'value' },
            { title: 'key', value: 'value' }
        ]
    }]
}

Could anyone point out what exactly is my mistake in the function, please?

3 Answers 3

2

I think you've chosen the wrong base case for the tree recursion. Put the leaf detection at the top of the function, not in the loop:

function transform(title, value) {
    if (typeof value === 'string') {
        return {title, value};
    } else {
        const children = [];
        for (let key in obj) {
            children.push(transform(key, obj[key]));
        }
        return {title, children};
    }
}

Since you only want the single child of the root node, you'd call it as

console.log(transform('parent', data.parent));

or

console.log(transform('', data).children[0]);
Sign up to request clarification or add additional context in comments.

Comments

1

Here is what you want:

const o = {
    parent: {
        child1: {
            key: 'value'
        },
        child2: {
            key: 'value'
        },
        child3: {
            key: 'value'
        }
    }
};
const r = {};
const struct = (root, c) => {
    Object.entries(root).map(([k, v]) => {
        if (typeof v === 'object') {
            const el = { title: k, children: [] };
            c.push(el);
            struct(v, el.children);
        } else {
            c.push({ title: k, value: v });
        }
    });
}
r.title = 'parent';
r.children = [];
struct(o.parent, r.children);
console.log(r);

Comments

0

While the recursion here is fairly simple, you have an odd requirement. What would you expect if the root node had multiple properties?

I handle this with a wrapper function which uses the recursive one to build an array of similar nodes, but then takes only the first result. This seems far from ideal, but it works:

const _transform = (obj) =>
  Object .entries (obj) .map (([k, v]) => ({
    title: k, 
    ...(Object (v) === v ? {children: _transform (v)} : {value: v})
  }))

const transform = (obj) => _transform (obj) [0]

const input = {parent: {child1: {key: 'value'}, child2: {key: 'value'}, child3: {key: 'value'}}}

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

Alternate API

This code would be simpler with the API suggested by Bergi. This is the same idea as that code but with an implementation in my style:

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

const input = {parent: {child1: {key: 'value'}, child2: {key: 'value'}, child3: {key: 'value'}}}

console .log (
  transform ('parent', input.parent)
)
.as-console-wrapper {min-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.