-
-
Save harish2704/d0ee530e6ee75bad6fd30c98e5ad9dab to your computer and use it in GitHub Desktop.
/* Implementation of lodash.get function */ | |
function getProp( object, keys, defaultVal ){ | |
keys = Array.isArray( keys )? keys : keys.split('.'); | |
object = object[keys[0]]; | |
if( object && keys.length>1 ){ | |
return getProp( object, keys.slice(1) ); | |
} | |
return object === undefined? defaultVal : object; | |
} | |
/* Implementation of lodash.set function */ | |
function setProp( object, keys, val ){ | |
keys = Array.isArray( keys )? keys : keys.split('.'); | |
if( keys.length>1 ){ | |
object[keys[0]] = object[keys[0]] || {}; | |
return setProp( object[keys[0]], keys.slice(1), val ); | |
} | |
object[keys[0]] = val; | |
} |
I guess return object ? object : defaultVal
is more cleaner than return object === undefined? defaultVal : object;
It is clearer but those aren't the same statements.
// I'm also comparing _.get here
function getProp(object, keys, defaultVal) {
keys = Array.isArray(keys) ? keys : keys.split('.');
object = object[keys[0]];
if (object && keys.length > 1) {
return getProp(object, keys.slice(1), defaultVal);
}
return object === undefined ? defaultVal : object;
}
function getPropClearer(object, keys, defaultVal) {
keys = Array.isArray(keys) ? keys : keys.split('.');
object = object[keys[0]];
if (object && keys.length > 1) {
return getPropClearer(object, keys.slice(1), defaultVal);
}
return object ? object : defaultVal;
}
var testEmptyString = { x: '' }
console.log(
getProp(testEmptyString, 'x', 'DEFAULT_VAL'), // => ''
_.get(testEmptyString, 'x', 'DEFAULT_VAL'), // => ''
getPropClearer(testEmptyString, 'x', 'DEFAULT_VAL') // => 'DEFAULT_VAL'
)
var testNull = { x: null }
console.log(
getProp(testNull, 'x', 'DEFAULT_VAL'), // => null
_.get(testNull, 'x', 'DEFAULT_VAL'), // => null
getPropClearer(testNull, 'x', 'DEFAULT_VAL') // => 'DEFAULT_VAL'
)
var testNestedFalse = { x: { y: false } }
console.log(
getProp(testNestedFalse, 'x.y', 'DEFAULT_VAL'), // => false
_.get(testNestedFalse, 'x.y', 'DEFAULT_VAL'), // => false
getPropClearer(testNestedFalse, 'x.y', 'DEFAULT_VAL') // => 'DEFAULT_VAL'
)
Here you can find pretty nice explanation what's going on.
This version can also support array syntax
Like a[0].b.c
__get(object, keys, defaultVal = null): any {
keys = Array.isArray(keys) ? keys : keys.replace(/(\[(\d)\])/g, '.$2').split('.');
object = object[keys[0]];
if (object && keys.length > 1) {
return this.__get(object, keys.slice(1), defaultVal);
}
return object === undefined ? defaultVal : object;
}
This version is slightly optimized and also supports root selectors (getProp({ foo: 'bar'}, ''
=> { foo:bar }
):
/**
* getProp utility - an alternative to lodash.get
* @author @harish2704, @muffypl, @pi0
* @param {Object} object
* @param {String|Array} path
* @param {*} defaultVal
*/
function getProp (object, path, defaultVal) {
const _path = Array.isArray(path)
? path
: path.split('.').filter(i => i.length)
if (!_path.length) {
return object === undefined ? defaultVal : object
}
return getProp(object[_path.shift()], _path, defaultVal)
}
The above version has an issue when object is undefined and _path length > 0, an improvement here:
/**
* getProp utility - an alternative to lodash.get
* @author @harish2704, @muffypl, @pi0, @imnnquy
* @param {Object} object
* @param {String|Array} path
* @param {*} defaultVal
*/
function getProp (object, path, defaultVal) {
const _path = Array.isArray(path)
? path
: path.split('.').filter(i => i.length)
if (!_path.length) {
return object === undefined ? defaultVal : object
}
return getProp(object[_path.shift()], _path, defaultVal)
}
that's will be better.
function getProp (object, path, defaultVal) {
const PATH = Array.isArray(path)
? path
: path.split('.').filter(i => i.length);
if (!PATH.length) {
return object === undefined ? defaultVal : object;
}
if (object === null || object === undefined || typeof (object[PATH[0]]) === 'undefined') {
return defaultVal;
}
return getProp(object[PATH.shift()], PATH, defaultVal);
}
that's will be better
/**
* Gets the value at `path` of `object`.
* @param {Object} object
* @param {string|Array} path
* @returns {*} value if exists else undefined
*/
const get = (object, path) => {
if (typeof path === "string") path = path.split(".").filter(key => key.length);
return path.reduce((dive, key) => dive && dive[key], object);
};
The above version doesn't support some formats supported by lodash.get
.
Like: get(obj, ['forecast', 0, 'main.temp'], null)
But this version do it:
function get (object, path, value) {
const pathArray = Array.isArray(path) ? path : path.split('.').filter(key => key)
const pathArrayFlat = pathArray.flatMap(part => typeof part === 'string' ? part.split('.') : part)
return pathArrayFlat.reduce((obj, key) => obj && obj[key], object) || value
}
The above version don't support some formats supported by
lodash.get
.Like:
get(obj, ['forecast', 0, 'main.temp'], null)
But this version do it:
function get (object, path, value) { const pathArray = Array.isArray(path) ? path : path.split('.').filter(key => key) const pathArrayFlat = pathArray.flatMap(part => typeof part === 'string' ? part.split('.') : part) return pathArrayFlat.reduce((obj, key) => obj && obj[key], object) || value }
If return value is falsy it will not play properly.
function get (object, path, value) {
const pathArray = Array.isArray(path) ? path : path.split('.').filter(key => key);
const pathArrayFlat = pathArray.flatMap(part => typeof part === 'string' ? part.split('.') : part);
const checkValue = pathArrayFlat.reduce((obj, key) => obj && obj[key], object);
return checkValue === undefined ? value : checkValue
}
Sorry @Sooro1024, but I don't think it's necessary!
Did you notice the ||
in my return?
return pathArrayFlat.reduce((obj, key) => obj && obj[key], object) || value
If the reduce
result is falsy (undefined
inclusive), the value
is returned.
So, if the expected return is a number, just pass a value = 0
. And if a boolean is expected, just pass a value = false
.
So, I think extra code is not necessary. Just take some Node or DevTools console test:
And here is a use test:
@pi0, you solution throws an error, but thanks for inspiration for the most performant solution from provided here.
const getPropByPath = (object, path, defaultValue) => {
const _path = Array.isArray(path)
? path
: path.split('.');
if (object && _path.length) return getPropByPath(object[_path.shift()], _path, defaultValue);
return object === undefined ? defaultValue : object;
};
// without default value
const getPropByPath = (object, path) => {
const _path = Array.isArray(path)
? path
: path.split('.');
if (object && _path.length) return getPropByPath(object[_path.shift()], _path);
return object;
};
Benchmarks: https://jsbench.me/ogkwc7fxlg/1. I didn't include implementations out of scope of original lodash.get and those which iterate over whole path without break - i.e. reduce-based - they're too slow.
@pi0, you solution throws an error, but thanks for inspiration for the most performant solution from provided here.
const getPropByPath = (object, path, defaultValue) => { const _path = Array.isArray(path) ? path : path.split('.'); if (object && _path.length) return getPropByPath(object[_path.shift()], _path, defaultValue); return object === undefined ? defaultValue : object; }; // without default value const getPropByPath = (object, path) => { const _path = Array.isArray(path) ? path : path.split('.'); if (object && _path.length) return getPropByPath(object[_path.shift()], _path); return object; };Benchmarks: https://jsbench.me/ogkwc7fxlg/1. I didn't include implementations out of scope of original lodash.get and those which iterate over whole path without break - i.e. reduce-based - they're too slow.
This solution fixed for me a problem: Empty string counts as undefined.
I'd like the empty string back. It's not the same (for me).
I'm talking about the version without default value.
setProp is not work.
I created a fork of this version that includes tests, handles falsey values (including undefined
), and handles objects-inside-arrays (i.e. '[0].id'
) as well as arrays-inside-objects (i.e. 'a.b[0].c'
):
https://gist.github.com/andrewchilds/30a7fb18981d413260c7a36428ed13da
many thanks
Function
getProp
contains error. Look at the example below:You don't pass down
defaultVal
to recursive call. Fixed version: