How to update value of nested array of objects

11,944

Solution 1

You could use an iterative and recursice approach with a named function as callback for Array#forEach.

let mainArray = [
{
    value: '/AG_TURF-123', 
    label: 'Ag & Turf', 
    checked: false, 
    id: 123, 
    children: [
        { 
            value: '/AG_TURF-123/TRACTOR-456', 
            label: 'Tractors', 
            checked: false, 
            id: 456, 
            children: [
                { 
                    value: '/AG_TURF-123/TRACTOR-456/Large-7-8-9-series', 
                    label: 'Large (7, 8, 9) Series', 
                    checked: false, 
                    id: 789, 
                    children: [
                        { 
                            value: '/AG_TURF-123/TRACTOR-456/Large-7-8-9-series/7-family-tractor', 
                            label: '7 Family Tractor', 
                            checked: false, 
                            id: 101112 
                        }, 
                        { 
                            value: '/AG_TURF-123/TRACTOR-456/Large-7-8-9-series/8-family-tractor', 
                            label: '8 Family Tractor', 
                            checked: false, 
                            id: 131415 
                        }, 
                        { 
                            value: '/AG_TURF-123/TRACTOR-456/Large-7-8-9-series/9-family-tractor', 
                            label: '9 Family Tractor', 
                            checked: false, 
                            id: 161718 
                        }
                    ] 
                }, 
                {
                    value: '/app/Http/routes.js', 
                    label: 'routes.js', 
                    checked: false, 
                    id: 181920 
                }
            ]
        }, 
        { 
            value: '/app/Providers', 
            label: 'Providers', 
            checked: false, 
            id: 212223, 
            children: [
                { 
                    value: '/app/Http/Providers/EventServiceProvider.js', 
                    label: 'EventServiceProvider.js', 
                    checked: false, 
                    id: 242526 
                }
            ]
        }
    ]
}, 
{
    value: '/config', 
    label: 'config', 
    checked: false, 
    id: 272829, 
    children: [
        {
            value: '/config/app.js', 
            label: 'app.js', 
            checked: false, 
            id: 303132
        },
        {
            value: '/config/database.js', 
            label: 'database.js', 
            checked: false, 
            id: 333435 
        }
    ]
}, 
{
    value: '/public', 
    label: 'public', 
    checked: false, 
    id: 353637, 
    children: [
        {
            value: '/public/assets/', 
            label: 'assets', 
            checked: false, 
            id: 383940, 
            children: [
                {
                    value: '/public/assets/style.css', 
                    label: 'style.css', 
                    checked: false, 
                    id: 404142
                }
            ]
        }, 
        {
            value: '/public/index.html', 
            label: 'index.html', 
            checked: false, 
            id: 434445 
        }
    ] 
}, 
{ 
    value: '/.env', 
    label: '.env', 
    checked: false, 
    id: 464748 
}, 
{ 
    value: '/.gitignore', 
    label: '.gitignore', 
    checked: false, 
    id: 495051 
}, 
{ 
    value: '/README.md', 
    label: 'README.md', 
    checked: false, 
    id: 525354 
}
],
    ids = [525354, 123, 131415];

mainArray
    .forEach(
        function iter(a) {
            if (ids.includes(a.id)) {
                a.checked = true;
            }
            Array.isArray(a.children) && a.children.forEach(iter);
        }
    );

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

Solution 2

If your ids are "unique" (there's only one unique object for each id property), you could also split this action in to two steps:

  1. Make a new data structure that flattens all items in to one object of id: item
  2. Loop through the ids to retrieve the items from this object.

If you find yourself looking up many objects by id, it might be easier to create this interim representation.

The creation of the map uses recursion similar to the other answer: whenever an item contains a children array, it adds those to the map object before it returns.

const mainArray = [{ value: '/AG_TURF-123', label: 'Ag & Turf', checked: false, id: 123, children: [{ value: '/AG_TURF-123/TRACTOR-456', label: 'Tractors', checked: false, id: 456, children: [{ value: '/AG_TURF-123/TRACTOR-456/Large-7-8-9-series', label: 'Large (7, 8, 9) Series', checked: false, id: 789, children: [{ value: '/AG_TURF-123/TRACTOR-456/Large-7-8-9-series/7-family-tractor', label: '7 Family Tractor', checked: false, id: 101112 }, { value: '/AG_TURF-123/TRACTOR-456/Large-7-8-9-series/8-family-tractor', label: '8 Family Tractor', checked: false, id: 131415 }, { value: '/AG_TURF-123/TRACTOR-456/Large-7-8-9-series/9-family-tractor', label: '9 Family Tractor', checked: false, id: 161718 }] }, { value: '/app/Http/routes.js', label: 'routes.js', checked: false, id: 181920 }] }, { value: '/app/Providers', label: 'Providers', checked: false, id: 212223, children: [{ value: '/app/Http/Providers/EventServiceProvider.js', label: 'EventServiceProvider.js', checked: false, id: 242526 }] }] }, { value: '/config', label: 'config', checked: false, id: 272829, children: [{ value: '/config/app.js', label: 'app.js', checked: false, id: 303132 }, { value: '/config/database.js', label: 'database.js', checked: false, id: 333435 }] }, { value: '/public', label: 'public', checked: false, id: 353637, children: [{ value: '/public/assets/', label: 'assets', checked: false, id: 383940, children: [{ value: '/public/assets/style.css', label: 'style.css', checked: false, id: 404142 }] }, { value: '/public/index.html', label: 'index.html', checked: false, id: 434445 }] }, { value: '/.env', label: '.env', checked: false, id: 464748 }, { value: '/.gitignore', label: '.gitignore', checked: false, id: 495051 }, { value: '/README.md', label: 'README.md', checked: false, id: 525354 }];

// We reduce the array of nested items in to one object of:
// { id: item }
const idMap = mainArray.reduce(function merge(map, node) {
  map[node.id] = node;
  
  if (Array.isArray(node.children)) {
    node.children.reduce(merge, map);
  }
  
  return map;
}, {});

const ids = [525354, 123, 131415];

// Whenever you need an item, you can get it
// using idMap[id]
const items = ids.map(id => idMap[id]);
items.forEach(item => item.checked = true);

// or: ids.forEach(id => idMap[id].checked = true)
Share:
11,944
Hashir Hussain
Author by

Hashir Hussain

Updated on June 05, 2022

Comments

  • Hashir Hussain
    Hashir Hussain almost 2 years

    This is my actual array :

    let mainArray= [
        {
            value: '/AG_TURF-123',
            label: 'Ag & Turf',
            checked: false,
            id:123,
            children: [
                {
                    value: '/AG_TURF-123/TRACTOR-456',
                    label: 'Tractors',
                    checked: false,
                    id:456,
                    children: [
                        {
                            value: '/AG_TURF-123/TRACTOR-456/Large-7-8-9-series',
                            label: 'Large (7, 8, 9) Series',
                            checked: false,
                            id:789,
                            children: [{
                                value: '/AG_TURF-123/TRACTOR-456/Large-7-8-9-series/7-family-tractor',
                                label: '7 Family Tractor',
                                checked: false,
                                id:101112
                            },{
                                value: '/AG_TURF-123/TRACTOR-456/Large-7-8-9-series/8-family-tractor',
                                label: '8 Family Tractor',
                                checked: false,
                                id:131415
                            },{
                                value: '/AG_TURF-123/TRACTOR-456/Large-7-8-9-series/9-family-tractor',
                                label: '9 Family Tractor',
                                checked: false,
                                id:161718
                            }],
                        },
                        {
                            value: '/app/Http/routes.js',
                            label: 'routes.js',
                            checked: false,
                            id:181920
                        },
                    ],
                },
                {
                    value: '/app/Providers',
                    label: 'Providers',
                    checked: false,
                    id:212223,
                    children: [{
                        value: '/app/Http/Providers/EventServiceProvider.js',
                        label: 'EventServiceProvider.js',
                        checked: false,
                        id:242526
                    }],
                },
            ],
        },
        {
            value: '/config',
            label: 'config',
            checked: false,
            id:272829,
            children: [
                {
                    value: '/config/app.js',
                    label: 'app.js',
                    checked: false,
                    id:303132
                },
                {
                    value: '/config/database.js',
                    label: 'database.js',
                    checked: false,
                    id:333435
                },
            ],
        },
        {
            value: '/public',
            label: 'public',
            checked: false,
            id:353637,
            children: [
                {
                    value: '/public/assets/',
                    label: 'assets',
                    checked: false,
                    id:383940,
                    children: [{
                        value: '/public/assets/style.css',
                        label: 'style.css',checked: false,
                        id:404142
                    }],
                },
                {
                    value: '/public/index.html',
                    label: 'index.html',
                    checked: false,
                    id: 434445
                },
            ],
        },
        {
            value: '/.env',
            label: '.env',
            checked: false,
            id: 464748
        },
        {
            value: '/.gitignore',
            label: '.gitignore',
            checked: false,
            id: 495051
        },
        {
            value: '/README.md',
            label: 'README.md',
            checked: false,
            id: 525354
        },
    ];
    

    This is my list of value :

    const ids=[525354,123,131415];

    I want to set checked to true if id matched. My main array may go to 6 to 7 step deep.

    What I've done till yet :

    setCheckedFlagToItems(checkList, items) {
            return items.map((item) => {
                const node = item;
                if (checkList.indexOf(node.id) !== -1) {
                    node.checked = true;
                }
                if ((node.children) && (Array.isArray(node.children) && node.children.length > 0)) {
                    this.setCheckedFlagToItems(checkList, node.children);
                }
                return node;
            }, this);
        }
    

    But it not working as usual.