Last active
October 1, 2021 19:15
-
-
Save dfkaye/59263b51cf1e0b633181c5f44ae2066a to your computer and use it in GitHub Desktop.
_.get() polyfill for the lodash.js method not available in underscore.js ~ fairly q&d implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 28 June 2017 | |
// Quick-and-dirty implementation. | |
// 30 Sept 2021 | |
// Changes prompted by @nativekar: | |
// - Allow pathName param to be either Array or string. | |
// - Support "value[property]" notation. | |
// House-cleaning: | |
// - Move the inline mocha suite to own file, replace with inline test closure. | |
// - Add "_" inline definition for standalone test execution. | |
// Uncomment to set _ in test environment: | |
// var _ = globalThis._ ?? {}; | |
/** | |
* _.get() polyfill for q&d implementation. | |
* lodash has _.get(data, path, defaultValue), but underscore does not. | |
* makes data traversal so nice. | |
* see https://lodash.com/docs/4.17.4#get | |
*/ | |
typeof _.get == 'function' || (function() { | |
/** | |
* @param {object} object, The object to query. | |
* @param {Array|string} pathName: The path of the property to get. | |
* @param {*} [defaultValue]: Optional value to return for undefined resolved values. | |
* @returns {*} resolved value, or default value if specified. | |
*/ | |
var at = function get(object, pathName, defaultValue) { | |
// Coerce pathName to a string (even it turns into "[object Object]"). | |
var path = Array.isArray(pathName) | |
? pathName.join(".") | |
: String(pathName); | |
// Support bracket notation, e.g., "a[0].b.c". | |
var match = /\[\\?("|')?(\w|d)+\\?("|')?\]/g; | |
var parts = path | |
.replace(match, (m, i, v) => "." + v) | |
.split('.'); | |
var length = parts.length; | |
var i = 0; | |
// In case object isn't a real object, set it to undefined. | |
var value = object === Object(object) ? object : undefined; | |
while (value != null && i < length) { | |
value = value[parts[i++]]; | |
} | |
/** | |
* lodash.get() returns the resolved value if | |
* 1. iteration happened (i > 0) | |
* 2. iteration completed (i === length) | |
* 3. the value at the path is found in the data structure (not undefined). Note that if the path is found but the | |
* value is null, then null is returned. | |
* If any of those checks fails, return the defaultValue param, if provided. | |
*/ | |
return i && i === length && value !== undefined ? value : defaultValue; | |
}; | |
_.get = at; | |
}()); | |
/* test it out */ | |
~(function suite() { | |
// suite replaces inline mocha suite which has been moved to its own file. | |
var data = { a: { b: { c: { d: 'hello' } } } }; | |
var defaultValue = "I'm the default value"; | |
var unresolved = 'not resolved'; | |
var expected = 6; | |
var instance = Object(1); | |
var tests = [ | |
// should find value by pathName. | |
_.get(data, 'a.b.c.d') === 'hello', | |
// should return default value when pathName not resolved. | |
_.get({}, 'a.b.c.d', defaultValue) === defaultValue, | |
// should not resolve non-object data. | |
_.get('1', 'toString', unresolved) === unresolved, | |
// should not resolve with an invalid pathName. | |
_.get({ name: 'hello' }, '', unresolved) === unresolved, | |
// should resolve on arrays with valid index pathName. | |
_.get([1, 2, [3, 4, [5, expected]]], '2.2.1', expected) === expected, | |
// should resolve on coerced-to-an-object with valid pathName. | |
_.get(instance, 'toString').call(instance) === "1", | |
// should resolve pathName as an Array of descendant property names. | |
_.get(data, ['a', 'b', 'c', 'd']) === 'hello', | |
// should resolve pathName containing bracket property names. | |
_.get(data, 'a["b"]["c"]["d"]') === 'hello', | |
// should resolve pathName containing bracket property names. | |
_.get(data, ['a["b"]', 'c', 'd']) === 'hello' | |
]; | |
// Should print array with all values `true`. | |
console.log( tests ); | |
}()); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 30 Sept 2021 | |
// moved the inline mocha suite to own file | |
describe('_.get() method', function() { | |
/** | |
* Verification of the _.get() utility method. | |
* using mocha and chai.assert | |
*/ | |
it('should find value by path', function (done) { | |
var data = { | |
a: { | |
b: { | |
c: { | |
d: 'hello' | |
} | |
} | |
} | |
}; | |
var value = _.get(data, 'a.b.c.d'); | |
assert.equal(value, 'hello'); | |
done(); | |
}); | |
it('should return default value when path not resolved', function (done) { | |
var defaultValue = "I'm the default value"; | |
var value = _.get({}, 'a.b.c.d', defaultValue); | |
assert.equal(value, defaultValue); | |
done(); | |
}); | |
it('should not resolve non-object data', function (done) { | |
var unresolved = 'not resolved'; | |
var value = _.get('1', 'toString', unresolved); | |
assert.equal(value, unresolved); | |
done(); | |
}); | |
it('should not resolve with an invalid pathString', function (done) { | |
var unresolved = 'not resolved'; | |
var value = _.get({ name: 'hello' }, '', unresolved); | |
assert.equal(value, unresolved); | |
done(); | |
}); | |
it('should resolve on arrays with valid index pathString', function(done) { | |
var expected = 6; | |
var value = _.get([1, 2, [3, 4, [5, expected]]], '2.2.1', expected); | |
assert.equal(value, expected); | |
done(); | |
}); | |
it('should resolve on coerced-to-an-object with valid pathString', function (done) { | |
var instance = Object(1); | |
var value = _.get(instance, 'toString'); | |
// it's a function, try to call it | |
assert.strictEqual(value.call(instance), '1'); | |
assert.notStrictEqual(value.call(instance), 1); | |
// coerced toString output | |
assert.equal(value, 'function toString() { [native code] }'); | |
done(); | |
}); | |
}); | |
Cheers! Look forward to the fix. (I have started working on one of mine, maybe we can compare notes)
@nativekar - OK, I've updated the method to accept arrays as pathName
params, added support for value[property]
notation, and redone the inline tests as a closure (and moved the mocha suite to its own file). Let me know if you have questions.
@dfkaye - this looks perfect. Thanks for working on this.
@nativekar - Thanks. So, I looked up underscore docs to see if they had a get method now - turns out they do - https://underscorejs.org/#get - oh well…
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@nativekar I wasn’t aware of that. But then I haven’t used this in a long time. Might fix this in a couple days.
Thanks for pointing it out.