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

Viewed 4170

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
            },
        },
    }
}
3 Answers

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.

In our project we are using immutability-helper. It allows to wrtite queries like the ones in MongoDB. Really worth checking out!

You can try Ramda assocPath or set from lodash/fp

Both are immutable

R.assocPath(['c124', 'fields', 'f4', 'value'], 'white', _companies);
_.set(['c124', 'fields', 'f4', 'value'], 'white', _companies);
_.set('c124.fields.f4.value', 'white', _companies);
Related