Search code examples
javascriptecmascript-6lodash

A better way to deep update an object in ES6 with lodash or any other library


Looking for a way to deep update/merge for tests:

Currently, we're doing this:

 {
    ..._companies,
    ['c124']: {
        ..._companies.c124,
        ['fields']: {
            ..._companies.c124.fields,
            ['f4']: {
                ..._companies.c124.fields.f4,
                value: 'white'
            }
        }
    }

When ideally we would do this:

update_merge(_companies, {
    c124: {
        fields: {
            f4: {
                value:'white'
            }
        }
    }
})

Simple deep merge doesn't work in this case as it completely replaces the c124 key.

The original _companies snippet:

const companies = {
    c123: {
        id: 123,
        name: 'Company one',
        fields: {...} // fields                   
    },
    c124: {
        id: 124,
        name: 'Company two',
        fields: {
            f1: {...}, //...fields                   
            f2: {...}, //...fields                   
            f3: {...}, //...fields                   
            f4: {
                id: 4,
                fieldType: "shortText",longText
                title: "favorite color",
                value: 'green', // <==== only this will change
            },
        },
    }
}

This expected result:

const companies = {
    c123: {
        id: 123,
        name: 'Company one',
        fields: {...} // fields                   
    },
    c124: {
        id: 124,
        name: 'Company two',
        fields: {
            f1: {...}, //...fields                   
            f2: {...}, //...fields                   
            f3: {...}, //...fields                   
            f4: {
                id: 4,
                fieldType: "shortText",longText
                title: "favorite color",
                value: 'white', // <==== this changed
            },
        },
    }
}

Solution

  • Lodash's _.merge() is recursive, and allows you to update nested keys

    Note: I'm changing the value from blue to red

    const companies = {"c123":{"id":123,"name":"Company one","fields":{}},"c124":{"id":124,"name":"Company two","fields":{"f1":{},"f2":{},"f3":{},"f4":{"id":4,"fieldType":"shortText","title":"favorite color","value":"blue"}}}};
    
    const newCompanies = _.merge({}, companies, {
        c124: {
            fields: {
                f4: {
                    value:'red'
                }
            }
        }
    })
    
    console.log(newCompanies);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

    Another option is to use lodash's _.set() if you can mutate the object:

    const companies = {"c123":{"id":123,"name":"Company one","fields":{}},"c124":{"id":124,"name":"Company two","fields":{"f1":{},"f2":{},"f3":{},"f4":{"id":4,"fieldType":"shortText","title":"favorite color","value":"blue"}}}};
    
    _.set(companies, ['c124', 'fields', 'f4', 'value'], 'red')
    
    console.log(companies);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

    However, if you do that often, and need an immutable object (in redux for example), I would suggest normalizing the tree to have a shallower more accessible leaves.