Given an object, a path in the string or array of strings format, and a value, update the value at the given path in the object.
This is a polyfill for lodash._set() method and is opposite of lodash._get() method.
Example
const object = { 'a': [{ 'b': { 'c': 3 } }] }; set(object, 'a[0].b.c', 4); console.log(object.a[0].b.c); // 4 set(object, ['x', '0', 'y', 'z'], 5); console.log(object.x[0].y.z); // 5
To implement this function, we will first check if the provided path is a string or an array of strings.
If it is string then filter all the special characters like [
, ]
and split the string on .
to get all the path keys in an array.
Then using a helper function we can assign the value to the provided path.
- Get only the first key from the path array and aggregate the rest of the keys.
- If there are no more keys left to update, assign the value to the current key.
- Else recursively call the same function with the current value for the next path.
- While moving to the next path, check the type of key, if it is numeric the value should be an array thus pass array, else if it is a string pass the object.
Note:- This will override the existing value and assign a new one.
const helper = (obj, path, value) => { // get the current and the remaining keys from the path let [current, ...rest] = path; // if there are more keys // add the value as an object or array // depending upon the typeof key if(rest.length > 0){ // if there is no key present // create a new one if(!obj[current]){ // if the key is numeric // add an array // else add an object const isNumber = `${+rest[0]}` === rest[0]; obj[current] = isNumber ? [] : {}; } // recurisvely update the remaining path // if the last path is not of object type // but key is then // create an object or array based on the key // and update the value if(typeof obj[current] !== 'object'){ // determine if the key is string or numeric const isNumber = `${+rest[0]}` === rest[0]; obj[current] = helper(isNumber ? [] : {}, rest, value) } // else directly update value else{ obj[current] = helper(obj[current], rest, value); } } // else directly assing the value to the key else{ obj[current] = value; } // return the updated obj return obj; } const set = (obj, path, value) => { let pathArr = path; // if path is of string type // replace the special characters // and split the string on . to get the path keys array if(typeof path === 'string'){ pathArr = path.replace('[', '.').replace(']', '').split("."); } // use the helper function to update helper(obj, pathArr, value); };
Input: const abc = { a: { b: { c: [1, 2, 3] }, d: { a: "hello" } } }; const instance1 = JSON.parse(JSON.stringify(abc)); set(instance1, 'a.b.c', 'learnersbucket'); console.log(instance1.a.b.c); const instance2 = JSON.parse(JSON.stringify(abc)); set(instance2, 'a.b.c.0', 'learnersbucket'); console.log(instance2.a.b.c[0]); const instance3 = JSON.parse(JSON.stringify(abc)); set(instance3, 'a.b.c[1]', 'learnersbucket'); console.log(instance3.a.b.c[1]); const instance4 = JSON.parse(JSON.stringify(abc)); set(instance4, ['a', 'b', 'c', '2'], 'learnersbucket'); console.log(instance4.a.b.c[2]); const instance5 = JSON.parse(JSON.stringify(abc)); set(instance5, 'a.b.c[3]', 'learnersbucket'); console.log(instance5.a.b.c[3]) const instance6 = JSON.parse(JSON.stringify(abc)); set(instance6, 'a.c.d[0]', 'learnersbucket'); // valid digits treated as array elements console.log(instance6.a.c.d[0]); const instance7 = JSON.parse(JSON.stringify(abc)); set(instance7, 'a.d.01', 'learnersbucket'); // invalid digits treated as property string console.log(instance7.a.d['01']); const object = { 'a': [{ 'b': { 'c': 3 } }] }; set(object, 'a[0].b.c', 4); console.log(object.a[0].b.c); set(object, ['x', '0', 'y', 'z'], 5); console.log(object.x[0].y.z); Output: "learnersbucket" "learnersbucket" "learnersbucket" "learnersbucket" "learnersbucket" "learnersbucket" "learnersbucket" 4 5