AlexNikolaev94
AlexNikolaev94

Reputation: 1209

Object transformation using recursive function

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?

Upvotes: 1

Views: 113

Answers (3)

Scott Sauyet
Scott Sauyet

Reputation: 50797

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}

Upvotes: 0

Bergi
Bergi

Reputation: 664579

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]);

Upvotes: 2

8HoLoN
8HoLoN

Reputation: 1142

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);

Upvotes: 1

Related Questions