-
-
Save alvaropinot/39fdc70739646114f5708f5aafb96222 to your computer and use it in GitHub Desktop.
function _get(property, defaultValue) { | |
var path = property.split(/[\.\["'\]]+/); | |
return function(obj) { | |
return typeof obj === 'object' ? path.reduce(function (acc, key) { | |
var value = acc && acc[key]; | |
// should allow null and falsy values. | |
return (value || (value !== undefined)) ? | |
value : | |
defaultValue; | |
}, obj) : | |
obj; | |
} | |
} | |
test('should return a level one property', function () { | |
var obj = { | |
foo: 'foo' | |
}; | |
var actual = _get('foo')(obj), | |
expected = 'foo'; | |
equal(actual, expected); | |
}); | |
test('should return a level two property', function () { | |
var obj = { | |
foo: { | |
bar: 'bar' | |
} | |
}; | |
var actual = _get('foo.bar')(obj), | |
expected = 'bar'; | |
equal(actual, expected); | |
}); | |
test('called with no value', function () { | |
var actual = _get('foo.bar')(), | |
expected = undefined; | |
equal(actual, expected, 'should return undefined'); | |
}); | |
test('called with a primitive value instead of an object', function () { | |
var actual = _get('foo.bar')(2), | |
expected = 2; | |
equal(actual, expected, 'should return the value'); | |
}); | |
test('should allow a default value when a property its not defined', function () { | |
var obj = { | |
foo: { | |
bar: 'bar' | |
} | |
}; | |
var actual = _get('foo.biz', 5)(obj), | |
expected = 5; | |
equal(actual, expected, 'should return the default value'); | |
actual = _get('bar', 5)(obj); | |
expected = 5; | |
equal(actual, expected, 'should return the default value'); | |
}); | |
test('should work for keys with `null` values', function () { | |
var obj = { | |
foo: { | |
bar: null | |
} | |
}; | |
var actual = _get('foo.bar', 5)(obj), | |
expected = null; | |
equal(actual, expected, 'should return null'); | |
}); | |
test('should work for keys with `false` values', function () { | |
var obj = { | |
foo: { | |
bar: false | |
} | |
}; | |
var actual = _get('foo.bar', 5)(obj), | |
expected = false; | |
equal(actual, expected, 'should return false'); | |
}); | |
test('should work for keys with `0` values', function () { | |
var obj = { | |
foo: { | |
bar: 0 | |
} | |
}; | |
var actual = _get('foo.bar', 5)(obj), | |
expected = 0; | |
equal(actual, expected, 'should return 0'); | |
}); | |
test('should return default value for undefined keys', function () { | |
var obj = { | |
foo: { | |
bar: undefined | |
} | |
}; | |
var actual = _get('foo.bar', 5)(obj), | |
expected = 5; | |
equal(actual, expected, 'should return the default value'); | |
}); | |
test('should return default value for undefined keys', function () { | |
var obj = { | |
foo: { | |
bar: { | |
biz: 'biz' | |
} | |
} | |
}; | |
var actual = _get('foo.bar.biz', 5)(obj), | |
expected = 'biz'; | |
equal(actual, expected); | |
}); | |
// TODO: move to array tests module and clean those objs. | |
test('should work with deep arrays', function () { | |
var obj = { | |
foo: { | |
arr: [ | |
{}, | |
{ | |
subarr: [ | |
{}, | |
{ | |
name:'rita' | |
} | |
] | |
} | |
] | |
} | |
}; | |
var actual = _get('foo.arr[1].subarr[1].name')(obj), | |
expected = 'rita'; | |
equal(actual, expected); | |
}); | |
test('should work with deep arrays and undenined properties', function () { | |
var obj = { | |
foo: { | |
arr: [ | |
{}, | |
{ | |
subarr: [ | |
{}, | |
{ | |
name:'rita' | |
} | |
] | |
} | |
] | |
} | |
}; | |
var actual = _get('foo.arr[2].subarr[5].name')(obj), | |
expected = undefined; | |
equal(actual, expected); | |
}); | |
test('should work with deep arrays and return defaultValue', function () { | |
var obj = { | |
foo: { | |
arr: [ | |
{}, | |
{ | |
subarr: [ | |
{}, | |
{ | |
name:'rita' | |
} | |
] | |
} | |
] | |
} | |
}; | |
var actual = _get('foo.arr[2].subarr[5].name', 2)(obj), | |
expected = 2; | |
equal(actual, expected); | |
}); |
I was thinking about a npm package named get-puntillo, get-el-puntillo, get-puntito or something funny :D
By the way, there are like 100 getters like this one, not just the lodash one hahaha
hahaha, nice job merging those, I think get-puntillo sounds perfect!
There are lots of them...yeah...but we got ours!
get-puntillo in da house! make some noooise
hey man, I'm still getting "undefined" when using [""]
syntax
and "split" method still returning "empty string" as last element of the array when using [""]
. It sounds logic, because the expression always matches /\]/
.
Still thinking, this kind of approach fits better:
function _get(parent, nested, defaultValue) {
return typeof nested === 'string' ?
(typeof parent === 'object' &&
nested
.split(/[\.\["'\]]+/)
.reduce(function(acc, current) {
return /^$/.test(current) ? acc : acc && acc[current]; // checking for empty strings
}, parent)) || defaultValue: // return default if value is falsy
parent;
}
var ns = {a:{b:{c:0}}};
console.log(_get(ns, '')) // Object {a: Object}
console.log( _get(ns, 'a.b.')) // Object {c: 0}
console.log( _get(ns, 'a.b.d')) // undefined
console.log(_get(ns, 'a.b.c', 2)) // 2
console.log( _get(ns, 'u.b.c')) // undefined
console.log( _get(ns, 'u.b.c', 3)) // 3
Although we do not have memoization yet, we could implement it if needed with your "closure" style.
;)
PS: Cannot use "slice" because not all the cases for "split" return empty string as last element
another variant if you want a default value only if you pass it, and the current value is falsy:
function _get(parent, nested, defaultValue) {
var argsLength = [].slice.call(arguments).length; // get arguments length
var value = typeof nested === 'string' ?
(typeof parent === 'object' &&
nested
.split(/[\.\["'\]]+/)
.reduce(function(acc, current) {
return /^$/.test(current) ? acc : acc && acc[current]; // checking for empty strings
}, parent)) :
parent;
return argsLength === 3 ? value || defaultValue : value; // if defaultValue is passed, then return default only if value is falsy
}
how about something like
'a[2]["a"]'.split(/[\.\["'\]]+/).slice(0, -1)
instead of thatfilter
?