Skip to content

Commit

Permalink
Merge f03f3ac into 7ee13c8
Browse files Browse the repository at this point in the history
  • Loading branch information
jgonggrijp committed Sep 21, 2020
2 parents 7ee13c8 + f03f3ac commit 42157e3
Show file tree
Hide file tree
Showing 16 changed files with 212 additions and 76 deletions.
51 changes: 51 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@
<li data-name="defaults">- <a href="#defaults">defaults</a></li>
<li data-name="clone">- <a href="#clone">clone</a></li>
<li data-name="tap">- <a href="#tap">tap</a></li>
<li data-name="toPath">- <a href="#toPath">toPath</a></li>
<li data-name="has">- <a href="#has">has</a></li>
<li data-name="property">- <a href="#property">property</a></li>
<li data-name="propertyOf">- <a href="#propertyOf">propertyOf</a></li>
Expand Down Expand Up @@ -1753,6 +1754,56 @@ <h2 id="objects">Object Functions</h2>
.value();
=&gt; // [2, 200] (alerted)
=&gt; [4, 40000]
</pre>

<p id="toPath">
<b class="header">toPath</b><code>_.toPath(path)</code><br>
Ensures that <b>path</b> is an array. If <b>path</b> is a string, it is
wrapped in a single-element array; if it is an array already, it is
returned unmodified.
</p>
<pre>
_.toPath('key');
=&gt; ['key']
_.toPath(['a', 0, 'b']);
=&gt; ['a', 0, 'b'] // (same array)
</pre>
<p>
<tt>_.toPath</tt> is used internally in <tt>has</tt>, <tt>get</tt>,
<tt>invoke</tt>, <tt>property</tt>, <tt>propertyOf</tt> and
<tt>result</tt>, as well as in <a href="#iteratee"><b>iteratee</b></a>
and all functions that depend on it, in order to normalize deep
property paths. You can override <tt>_.toPath</tt> if you want to
customize this behavior, for example to enable Lodash-like string path
shorthands. Be advised that altering <tt>_.toPath</tt> will unavoidably
cause some keys to become unreachable; override at your own risk.
</p>
<pre>
// Support dotted path shorthands.
var originalToPath = _.toPath;
_.mixin({
toPath: function(path) {
return _.isString(path) ? path.split('.') : originalToPath(path);
}
});
_.get({a: [{b: 5}]}, 'a.0.b');
=&gt; 5
</pre>

<p id="get">
<b class="header">get</b><code>_.get(object, path, [default])</code><br>
Returns the specified property of <b>object</b>. <b>path</b> may be
specified as a simple key, or as an array of object keys or array
indexes, for deep property fetching. If the property does not exist or
is <tt>undefined</tt>, the optional <b>default</b> is returned.
</p>
<pre>
_.get({a: 10}, 'a');
=&gt; 10
_.get({a: [{b: 2}]}, ['a', 0, 'b']);
=&gt; 2
_.get({a: 10}, 'b', 100);
=&gt; 100
</pre>

<p id="has">
Expand Down
8 changes: 8 additions & 0 deletions modules/_toPath.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import _ from './underscore.js';
import './toPath.js';

// Internal wrapper for `_.toPath` to enable minification.
// Similar to `cb` for `_.iteratee`.
export default function toPath(path) {
return _.toPath(path);
}
12 changes: 12 additions & 0 deletions modules/get.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import toPath from './_toPath.js';
import deepGet from './_deepGet.js';
import isUndefined from './isUndefined.js';

// Get the value of the (deep) property on `path` from `object`.
// If any property in `path` does not exist or if the value is
// `undefined`, return `defaultValue` instead.
// The `path` is normalized through `_.toPath`.
export default function get(object, path, defaultValue) {
var value = deepGet(object, toPath(path));
return isUndefined(value) ? defaultValue : value;
}
11 changes: 3 additions & 8 deletions modules/has.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import isArray from './isArray.js';
import _has from './_has.js';
import { hasOwnProperty } from './_setup.js';
import toPath from './_toPath.js';

// Shortcut function for checking if an object has a given property directly on
// itself (in other words, not on a prototype). Unlike the internal `has`
// function, this public version can also traverse nested properties.
export default function has(obj, path) {
if (!isArray(path)) {
return _has(obj, path);
}
path = toPath(path);
var length = path.length;
for (var i = 0; i < length; i++) {
var key = path[i];
if (obj == null || !hasOwnProperty.call(obj, key)) {
return false;
}
if (!_has(obj, key)) return false;
obj = obj[key];
}
return !!length;
Expand Down
2 changes: 2 additions & 0 deletions modules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export { default as defaults } from './defaults.js';
export { default as create } from './create.js';
export { default as clone } from './clone.js';
export { default as tap } from './tap.js';
export { default as get } from './get.js';
export { default as has } from './has.js';
export { default as mapObject } from './mapObject.js';

Expand All @@ -70,6 +71,7 @@ export { default as mapObject } from './mapObject.js';
export { default as identity } from './identity.js';
export { default as constant } from './constant.js';
export { default as noop } from './noop.js';
export { default as toPath } from './toPath.js';
export { default as property } from './property.js';
export { default as propertyOf } from './propertyOf.js';
export { default as matcher,
Expand Down
5 changes: 3 additions & 2 deletions modules/invoke.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import restArguments from './restArguments.js';
import isFunction from './isFunction.js';
import isArray from './isArray.js';
import map from './map.js';
import deepGet from './_deepGet.js';
import toPath from './_toPath.js';

// Invoke a method (with arguments) on every item in a collection.
export default restArguments(function(obj, path, args) {
var contextPath, func;
if (isFunction(path)) {
func = path;
} else if (isArray(path)) {
} else {
path = toPath(path);
contextPath = path.slice(0, -1);
path = path[path.length - 1];
}
Expand Down
7 changes: 2 additions & 5 deletions modules/property.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import isArray from './isArray.js';
import shallowProperty from './_shallowProperty.js';
import deepGet from './_deepGet.js';
import toPath from './_toPath.js';

// Creates a function that, when passed an object, will traverse that object’s
// properties down the given `path`, specified as an array of keys or indices.
export default function property(path) {
if (!isArray(path)) {
return shallowProperty(path);
}
path = toPath(path);
return function(obj) {
return deepGet(obj, path);
};
Expand Down
10 changes: 4 additions & 6 deletions modules/propertyOf.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import isArray from './isArray.js';
import deepGet from './_deepGet.js';
import noop from './noop.js';
import get from './get.js';

// Generates a function for a given object that returns a given property.
export default function propertyOf(obj) {
if (obj == null) {
return function(){};
}
if (obj == null) return noop;
return function(path) {
return !isArray(path) ? obj[path] : deepGet(obj, path);
return get(obj, path);
};
}
4 changes: 2 additions & 2 deletions modules/result.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import isArray from './isArray.js';
import isFunction from './isFunction.js';
import toPath from './_toPath.js';

// Traverses the children of `obj` along `path`. If a child is a function, it
// is invoked with its parent as context. Returns the value of the final
// child, or `fallback` if any child is undefined.
export default function result(obj, path, fallback) {
if (!isArray(path)) path = [path];
path = toPath(path);
var length = path.length;
if (!length) {
return isFunction(fallback) ? fallback.call(obj) : fallback;
Expand Down
9 changes: 9 additions & 0 deletions modules/toPath.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import _ from './underscore.js';
import isArray from './isArray.js';

// Normalize a (deep) property `path` to array.
// Like `_.iteratee`, this function can be customized.
export default function toPath(path) {
return isArray(path) ? path : [path];
}
_.toPath = toPath;
22 changes: 22 additions & 0 deletions test/objects.js
Original file line number Diff line number Diff line change
Expand Up @@ -985,6 +985,28 @@
assert.ok(!_.has({a: child}, ['a', 'foo']), 'does not check the prototype of nested props.');
});

QUnit.test('get', function(assert) {
var stooge = {name: 'moe'};
assert.strictEqual(_.get(stooge, 'name'), 'moe', 'should return the property with the given name');
assert.strictEqual(_.get(null, 'name'), void 0, 'should return undefined for null values');
assert.strictEqual(_.get(void 0, 'name'), void 0, 'should return undefined for undefined values');
assert.strictEqual(_.get('foo', null), void 0, 'should return undefined for null object');
assert.strictEqual(_.get({x: null}, 'x'), null, 'can fetch null values');
assert.strictEqual(_.get(null, 'length'), void 0, 'does not crash on property access of non-objects');
assert.strictEqual(_.get(stooge, 'size', 10), 10, 'allows a fallback value for undefined properties');
assert.strictEqual(_.get(stooge, 'name', 10), 'moe', 'ignores the fallback value if the property is defined');

// Deep property access
assert.strictEqual(_.get({a: 1}, 'a'), 1, 'can get a direct property');
assert.strictEqual(_.get({a: {b: 2}}, ['a', 'b']), 2, 'can get a nested property');
assert.strictEqual(_.get({a: {b: 2}}, ['a', 'c'], 10), 10, 'allows a fallback value for undefined properties');
assert.strictEqual(_.get({a: {b: 2}}, ['a', 'b'], 10), 2, 'ignores the fallback value if the property is defined');
assert.strictEqual(_.get({a: false}, ['a']), false, 'can fetch falsy values');
assert.strictEqual(_.get({x: {y: null}}, ['x', 'y']), null, 'can fetch null values deeply');
assert.strictEqual(_.get({x: null}, ['x', 'y']), void 0, 'does not crash on property access of nested non-objects');
assert.strictEqual(_.get({x: 'y'}, []), void 0, 'returns `undefined` for a path that is an empty array');
});

QUnit.test('property', function(assert) {
var stooge = {name: 'moe'};
assert.strictEqual(_.property('name')(stooge), 'moe', 'should return the property with the given name');
Expand Down
7 changes: 7 additions & 0 deletions test/utility.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@
assert.strictEqual(_.noop('curly', 'larry', 'moe'), void 0, 'should always return undefined');
});

QUnit.test('toPath', function(assert) {
var key = 'xyz';
var path = [key];
assert.deepEqual(_.toPath(key), path, 'bare strings are wrapped in a single-element array');
assert.strictEqual(_.toPath(path), path, 'arrays are returned untouched');
})

QUnit.test('random', function(assert) {
var array = _.range(1000);
var min = Math.pow(2, 31);
Expand Down
69 changes: 43 additions & 26 deletions underscore-esm.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion underscore-esm.js.map

Large diffs are not rendered by default.

Loading

0 comments on commit 42157e3

Please sign in to comment.