Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

javascript implementation of lodash "set" method

Found this excellent code for _.get vanilla js implementation:

const get = (obj, path, defaultValue) => path.split(".")
.reduce((a, c) => (a && a[c] ? a[c] : (defaultValue || null)), obj)

Now I'm looking for _.set implementation, any help would be appreciated.

like image 903
Roni Gadot Avatar asked Feb 17 '19 13:02

Roni Gadot


2 Answers

I think this could cover it:

const set = (obj, path, value) => {
    if (Object(obj) !== obj) return obj; // When obj is not an object
    // If not yet an array, get the keys from the string-path
    if (!Array.isArray(path)) path = path.toString().match(/[^.[\]]+/g) || []; 
    path.slice(0,-1).reduce((a, c, i) => // Iterate all of them except the last one
         Object(a[c]) === a[c] // Does the key exist and is its value an object?
             // Yes: then follow that path
             ? a[c] 
             // No: create the key. Is the next key a potential array-index?
             : a[c] = Math.abs(path[i+1])>>0 === +path[i+1] 
                   ? [] // Yes: assign a new array object
                   : {}, // No: assign a new plain object
         obj)[path[path.length-1]] = value; // Finally assign the value to the last key
    return obj; // Return the top-level object to allow chaining
};

// Demo
var obj = { test: true };
set(obj, "test.1.it", "hello");
console.log(obj); // includes an intentional undefined value

It is a bit more complex than get, because there is some logic needed to create missing parts of the path in the object, to overwrite primitive values that stand in the way, and to determine whether a new child should better be an array or a plain object.

like image 109
trincot Avatar answered Nov 10 '22 08:11

trincot


Check this one:

/**
 * @example
 * const obj = {id:1, address: {city: 'Minsk', street: 'Prityckogo 12'}}
 * setByString(obj, 'address.city', 'Grodno'); obj.address.city => 'Grodno'
 * setByString(obj, ['address', 'city'], 'Grodno'); obj.address.city => 'Grodno'
 * setByString(obj, ['address', 'city', 'phones'], {mobile: 1234, home: 222}); obj.address.city.phones.home => 222
*/

/**
* @param    {any}   input
* @return   {boolean}
*/

const isObject = (input) => (
  null !== input && 
    typeof input === 'object' &&
    Object.getPrototypeOf(input).isPrototypeOf(Object)
)

 **/
 * @param   {object}    obj
 * @param   {string}    path
 * @param   {any}       value
 */

const setByString = (obj, path, value) => {
  const pList = Array.isArray(path) ? path : path.split('.');
  const len = pList.length;
  // changes second last key to {}
  for (let i = 0; i < len - 1; i++) {
    const elem = pList[i];
    if (!obj[elem] || !isObject(obj[elem])) {
      obj[elem] = {};
    }
    obj = obj[elem];
  }

  // set value to second last key
  obj[pList[len - 1]] = value;
};
like image 27
grey87 Avatar answered Nov 10 '22 08:11

grey87