Tim E.
Tim E.

Reputation: 15

Changing nested object's structure

I want to convert an object from one format to another. So far my attempts at doing this recursively failed; either I'm getting a maximum stack exception or I'm unable to iterate over all paths.

Let's assume we have an object that lists questions and their answers. There may be N questions and M answers.

Object at start:

var before = {
    item: 'Question 1',
    id: '1',
    type: 'Question',
    items: [
        {
            item: 'Answer 1',
            id: '1.1',
            type: 'Answer',
            items:
            [
                {
                    item: 'Question 2',
                    id: '1.1.1',
                    type: 'Question',
                    items: [
                        {
                            item: 'Answer 2.1',
                            id: '1.1.1.1',
                            type: 'Answer'
                        },
                        {
                            item: 'Answer 2.2',
                            id: '1.1.1.2',
                            type: 'Answer'
                        }
                    ]
                }   
                // ...          
            ]
        }, {
            item: 'Answer 1',
            id: '1.2',
            type: 'Answer',
            items:
            [
                {
                    item: 'Question 3',
                    id: '1.2.1',
                    type: 'Question',
                    items: [
                        {
                            item: 'Answer 3.1',
                            id: '1.2.1.1',
                            type: 'Answer'
                        },
                        {
                            item: 'Answer 3.2',
                            id: '1.2.1.2',
                            type: 'Answer'
                        }
                    ]
                }   
                // ...          
            ]
        }
        // ...
    ]
}

Object how it should look like (wrap all in 'items' array; change key names 'item' to 'title', 'id' to 'key', remove 'type', add 'color' depending on 'type'):

var after = {
    items: [
        {
            title: 'Question 1',
            key: '1',
            color: 'Red',
            items: [
                {
                    title: 'Answer 1',
                    key: '1.1',
                    color: 'Blue',
                    items:
                    [
                        {
                            title: 'Question 2',
                            key: '1.1.1',
                            color: 'Red',
                            items: [
                                {
                                    title: 'Answer 2.1',
                                    key: '1.1.1.1',
                                    color: 'Blue'
                                },
                                {
                                    title: 'Answer 2.2',
                                    key: '1.1.1.2',
                                    color: 'Blue'
                                }
                            ]
                        }   
                        // ...          
                    ]
                }, {
                    title: 'Answer 1',
                    key: '1.2',
                    color: 'Blue',
                    items:
                    [
                        {
                            title: 'Question 3',
                            key: '1.2.1',
                            color: 'Red',
                            items: [
                                {
                                    title: 'Answer 3.1',
                                    key: '1.2.1.1',
                                    color: 'Blue'
                                },
                                {
                                    title: 'Answer 3.2',
                                    key: '1.2.1.2',
                                    color: 'Blue'
                                }
                            ]
                        }   
                        // ...          
                    ]
                }
                // ...
            ]
        }
    ]
}

It seems easy enough, but I can't get it to work. This is how I tried to iterate:

function iter(o) {
    for(let k in o) {
        if (!(['item', 'items', 'id'].includes(k))) // My object contains a few more keys I don't want to go down further into
            continue

        if (o[k] !== null && typeof o[k] === 'object') {
            iter(o[k]); // Max stack exception
            return;
        }
    }
};

Thank you very much!

Upvotes: 1

Views: 105

Answers (2)

drocha87
drocha87

Reputation: 629

You can achieve this using map, I wrote an simple test to show my point here

this is the important part of the code

function rename(item: any) {
  return {
    title: item.item,
    key: item.id,
    color: item.type === 'Question' ?  'red' : 'blue',
    items: item.items?.map(rename)
  }
}

console.log(items.map(rename))

Of course if you're using typescript, change any to the appropriate type and pay attention that I'm using ? operator which will not work with javascript, so you could do something like

...
items: item.items ? item.items.map(rename) : undefined
... 

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386746

You could map the objects and rename the keys and map nested items.

const
    iter = ({ item: title, id: key, type, items, ...o }) => ({
        title,
        key,
        color: 'color' + key,
        ...o,
        ...(items && { items: items.map(iter) })
    }),
    before = { item: 'Question 1', id: '1', type: 'Question', items: [{ item: 'Answer 1', id: '1.1', type: 'Answer', items: [{ item: 'Question 2', id: '1.1.1', type: 'Question', items: [{ item: 'Answer 2.1', id: '1.1.1.1', type: 'Answer' }, { item: 'Answer 2.2', id: '1.1.1.2', type: 'Answer' }] }] }, { item: 'Answer 1', id: '1.2', type: 'Answer', items: [{ item: 'Question 3', id: '1.2.1', type: 'Question', items: [{ item: 'Answer 3.1', id: '1.2.1.1', type: 'Answer' }, { item: 'Answer 3.2', id: '1.2.1.2', type: 'Answer' }] }] }] },
    result = [before].map(iter);
     
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 3

Related Questions