4

i really struggle with array.reduce() and i think in this case i'm not sure if i've got the right approach. Typically i have a starting array and i know what i need to end up with but i can't seem to get the grouping right.

This is the starting array

[
{ name: 'Home' },
{
    name: 'Services',
    menu: [
    { name: 'Painting' },
    { name: 'Decorating' },
    { name: 'Lawn mowing', submenu: 'Garden' },
    { name: 'Tree surgery', submenu: 'Garden' },
    { name: 'Edging', submenu: 'Garden' }
    ]
},
{ name: 'Contact' }
]

and what i'd like to end up with is this

[
{ name: 'Home' },
{
    name: 'Services',
    menu: [
    { name: 'Painting' },
    { name: 'Decorating' },
    {
        name: 'Garden',
        menu: [
        { name: 'Lawn mowing', submenu: 'Garden' },
        { name: 'Tree surgery', submenu: 'Garden' },
        { name: 'Edging', submenu: 'Garden' }
        ]
    }
    ]
},
{ name: 'Contact' }
]

So i'd like to be able to group by anything that has a submenu and then return a new sorted array.

2 Answers 2

2

Try the following recursive approach:

function reduce(array) {
    const result = [];
    // object to keep grouped submenus
    const grouped = {};
    for (let i of array) {
        if (i.menu) {
            // if the current item has a nested menu we call reduce recursively
            result.push({
                name: i.name,
                menu: reduce(i.menu)
            });
        } else if (i.submenu) {
            // if it has a submenu we put it to the grouped object
            if (grouped[i.submenu]) {
                grouped[i.submenu].menu.push(i)
            } else {
                grouped[i.submenu] = {
                    name: i.submenu,
                    menu: [i]
                };
                result.push(grouped[i.submenu]);
            }
        } else {
            // else we just copy it to the result array
            result.push(i);
        }
    }
    return result;
}

const array = [
    { name: 'Home' },
    {
        name: 'Services',
        menu: [
            { name: 'Painting' },
            { name: 'Decorating' },
            { name: 'Lawn mowing', submenu: 'Garden' },
            { name: 'Tree surgery', submenu: 'Garden' },
            { name: 'Edging', submenu: 'Garden' }
        ]
    },
    { name: 'Contact' }
];

console.log(reduce(array));

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

1 Comment

Thanks Kirill! this is awesome!
2

You could reduce the array by looking for submenu and take this for a seach of a node in the actual level.

If not just add either a new object or one with a menu from the recursive call.

function mapSubmenu(result, { name, menu, submenu }) {
    if (submenu) {
        var parent = result.find(({ name }) => name === submenu);
        if (!parent) result.push(parent = { name: submenu, menu: [] });
        parent.menu.push({ name, submenu });
    } else {
        result.push(menu
            ? { name, menu: menu.reduce(mapSubmenu, []) }
            : { name }
        );
    }
    return result;
}

var data = [{ name: 'Home' }, { name: 'Services', menu: [{ name: 'Painting' }, { name: 'Decorating' }, { name: 'Lawn mowing', submenu: 'Garden' }, { name: 'Tree surgery', submenu: 'Garden' }, { name: 'Edging', submenu: 'Garden' }] }, { name: 'Contact' }],
    result = data.reduce(mapSubmenu, []);

console.log(result);
.as-console-wrapper { max-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.