1

I am using lodash's get and set to manipulate complex objects. Although Lodash is a good library, it also weighs considerably (about 40kb). I am tring to develop a lean web-app, and lodash takes half of the bundle size.

How would you build safe functions that can replace get and set?

For instance, some function which will change the following object:

Set

const a = {b:2,c:{d:5}}

set(a,"c.d",7)

Which will result

//a = {b:2,c:{d:7}}.

if a = {}, it will result:

{c:{d:7}}

Get

const a = {b:2,c:{d:5}}

let x = get(a,"c.d",0)

Which will result

//x = 5 or if the path doesn't exist, //x = 0

Tal Yaron
  • 355
  • 2
  • 12
  • You can import lodash functions individually: [How to Import a Single Lodash Function?](https://stackoverflow.com/q/43479464), which will help save on space – Nick Parsons Feb 04 '21 at 09:45
  • 1
    For the get function, you can probably use something like: [Accessing nested JavaScript objects and arrays by string path](https://stackoverflow.com/a/6491621), you can probably repurpose that for the set function also – Nick Parsons Feb 04 '21 at 09:47
  • Thanks, I am importing them individually, and yet they take these 40K. – Tal Yaron Feb 04 '21 at 10:29
  • `lodash.get` should be replaced by optional chaining: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining – Con Antonakos May 03 '21 at 19:31

2 Answers2

1

You can build a recursive function in JavaScript to do that.

To set the data:

const obj = {b:7,c:{d:{f:8}}};
const set = (string, obj, value) => {
    const [current,...rest] = string.split(".");
    rest.length >= 1 ? set(rest.join("."), obj[current] = obj[current] || {}, value) : obj[current]= value;
    return obj; 
};

console.log(set("c.d.f", obj, 10));
gorak
  • 5,233
  • 1
  • 7
  • 19
  • Thank you for the solution. seems very smart. I didn't explain it correctly (and now, improved the explanation), I need it to behave like lodash get and set functions. See the above-improved explanation. – Tal Yaron Feb 04 '21 at 10:26
  • 1
    @TalYaron Updated my answer. Take a look. Now this will work even when the object is empty. And for the `get` you can take reference from link which @Nick has commented. – gorak Feb 04 '21 at 10:46
0

To replace both _.get() and _.set(), my team has come up with these methods. We have each in its own module and export them as the default method (importing sanitizePath.js into both get.js and set.js), but they can be included in a project in a variety of ways, so I'll present them together:

let rgxBracketToDot;

export function sanitizePath (path) {
    path = path || [];
    return Array.isArray(path) ? path : path.replace(rgxBracketToDot || (rgxBracketToDot = /\[(\w+)\]/g), '.$1').split('.');
}

export function get (obj, path) {
    if (!obj || typeof obj !== 'object') {
        return;
    }
    return sanitizePath(path).reduce((acc, val) => acc && acc[val], obj);
}

export function set (obj, path, value) {
    const [current,...rest] = sanitizePath(path);
    rest.length >= 1 ? set(obj[current] = obj[current] || {}, rest, value) : obj[current]= value;
    return obj;
}

Note that unlike lodash, sanitizePath() as is won't remove leading or trailing dots (.). To remove these and ensure 100% compatibility with lodash, you'll need to include these regex replaces:

path = path.replace(/^\./, '');
path = path.replace(/\.$/, '');

For maximum performance, it's best to set the regex pattern once in a let outside the method in a similar way as rgxBracketToDot. As none of the paths in our codebase begin or end with a dot we removed it for our purposes.

Likewise, it's best to only use array paths to avoid having to use sanitizeString() altogether. We plan on removing that step as well, but for the time being kept it in order to handle older config files.

adjwilli
  • 9,658
  • 4
  • 35
  • 29