Skip to content

Instantly share code, notes, and snippets.

@dfkaye
Last active October 1, 2021 19:15
Show Gist options
  • Save dfkaye/59263b51cf1e0b633181c5f44ae2066a to your computer and use it in GitHub Desktop.
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
// 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 );
}());
// 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();
});
});
@dzjin
Copy link

dzjin commented Mar 24, 2018

using this, thanks!

@nativekar
Copy link

nativekar commented Sep 30, 2021

The 2nd argument, pathString isn't always a String. Most of the time, it can also be an array.

@dfkaye
Copy link
Author

dfkaye commented Sep 30, 2021

@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.

@nativekar
Copy link

Cheers! Look forward to the fix. (I have started working on one of mine, maybe we can compare notes)

@dfkaye
Copy link
Author

dfkaye commented Sep 30, 2021

@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.

@nativekar
Copy link

@dfkaye - this looks perfect. Thanks for working on this.

@dfkaye
Copy link
Author

dfkaye commented Oct 1, 2021

@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