Deepdash
v5.1.0 - see changes
eachDeep, filterDeep, findDeep, someDeep, omitDeep, pickDeep, keysDeep etc.. Tree traversal library written in Underscore/Lodash fashion. Standalone or as a Lodash mixin extension
Deepdash lib is used in PlanZed.org - awesome cloud mind map app created by the author of deepdash. Plz check it, it's free and I need feedback 😉
List of Methods
- condense - condense sparse array
- condenseDeep - condense all the nested arrays
- eachDeep - (forEachDeep) iterate over all the children and sub-children
- exists - like a
_.hasbut returnsfalsefor empty array slots - filterDeep - deep filter object
- findDeep - returns first matching deep meta-value
- findValueDeep - returns first matching deep value
- findPathDeep returns path of the first matching deep value
- index - get an object with all the paths as keys and corresponding values
- paths - (keysDeep) get an array of paths
- mapDeep - produce an array of deep values processed by iteratee.
- mapValuesDeep - produce an object with the same structure but with values trasformed thru iteratee.
- mapKeysDeep - produce an object with the same values but with keys trasformed thru iteratee.
- reduceDeep - like reduce but deep
- someDeep - returns true if found some matching deep value, otherwise false
- pickDeep - get object only with keys specified by names or regexes
- omitDeep - get object without keys specified by names or regexes
- pathToString - convert an array to string path (opposite to _.toPath)
Installation
In a browser
Load script after Lodash, then pass a lodash instance to the deepdash function:
<script src="https://cdn.jsdelivr.net/npm/lodash/lodash.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/deepdash/browser/deepdash.min.js"></script>
<script>
deepdash(_);
console.log(_.eachDeep); // --> new methods mixed into Lodash
</script>
If you don't use Lodash - there is a standalone version:
<script src="https://cdn.jsdelivr.net/npm/deepdash/browser/deepdash.standalone.min.js"></script>
<script>
console.log(deepdash.eachDeep); // --> all the methods just work
</script>
Standalone Deepdash weighs more then "dry" version, because it includes some of cherry-picked Lodash methods it depends on. But it's better to use Standalone version, than include full Lodash just as dependency, if you don't need Lodash.
Using npm:
npm i --save deepdash
In Node.js:
// load Lodash if you need it
const _ = require('lodash');
//mixin all the methods into Lodash object
require('deepdash')(_);
// or cherry-pick method you only need and mix it into Lodash
require('deepdash/addFilterDeep')(_);
// or cherry-pick method separately if you don't want to mutate Lodash instance
const filterDeep = require('deepdash/getFilterDeep')(_);
// If you don't need Lodash - there is standalone version
const deepdash = require('deepdash/standalone'); // full
const filterDeep = require('deepdash/filterDeep'); // or separate standalone methods
There is also deepdash as ES6 module
npm i --save deepdash-es
import lodash from 'lodash-es';
import deepdash from 'deepdash-es';
const _ = deepdash(lodash);
in the ES package there are same cherry-pick and/or standalone methods as in the main package.
import filterDeep from 'deepdash-es/filterDeep';
or
import { filterDeep } from 'deepdash-es/standalone';
or
import _ from 'lodash-es';
import getFilterDeep from 'deepdash-es/getFilterDeep';
const filterDeep = getFilterDeep(_);
or
import _ from 'lodash-es';
import addFilterDeep from 'deepdash-es/addFilterDeep';
addFilterDeep(_);// --> _.filterDeep
Usage
let obj = {/* expand to see */};
let obj = {
a: {
b: {
c: {
d: [
{ i: 0 },
{ i: 1 },
{ i: 2 },
{ i: 3 },
{ i: 4 },
{ i: 5 },
{
o: {
d: new Date(),
f: function() {},
skip: {
please: {
dont: {
go: {
here: 'skip it',
},
},
},
},
},
},
],
s: 'hello',
},
b: true,
},
n: 12345,
u: undefined,
},
nl: null,
};
_.eachDeep(obj, (value, key, parent, context) => {
console.log(
_.repeat(' ', context.depth) +
key +
':' +
(value === null ? 'null' : typeof value),
context.parent && context.parent.path && ' @' + context.parent.path
);
if (key == 'skip') {
return false; // return false explicitly to skip iteration over current value's children
}
});
Console:
a:object
b:object @a
c:object @a.b
d:object @a.b.c
0:object @a.b.c.d
i:number @a.b.c.d[0]
1:object @a.b.c.d
i:number @a.b.c.d[1]
2:object @a.b.c.d
i:number @a.b.c.d[2]
3:object @a.b.c.d
i:number @a.b.c.d[3]
4:object @a.b.c.d
i:number @a.b.c.d[4]
5:object @a.b.c.d
i:number @a.b.c.d[5]
6:object @a.b.c.d
o:object @a.b.c.d[6]
d:object @a.b.c.d[6].o
f:function @a.b.c.d[6].o
skip:object @a.b.c.d[6].o
s:string @a.b.c
b:boolean @a.b
n:number @a
u:undefined @a
nl:null
Chaining works too:
_(obj).eachDeep((value, key, parent, context) => {/* do */}).value();
Demo
Example react+redux app with nested comments filtered by Deepdash.(source is here)
Methods
condense
Makes sparse array non-sparse. This method mutates object.
_.condense( arr ) => array
arr- array to condensereturns- 'condensed' array without holes.
Example:
let arr = ['a', 'b', 'c', 'd', 'e'];
delete arr[1];
console.log(arr);
delete arr[3];
console.log(arr);
_.condense(arr);
console.log(arr);
Console:
[ 'a', <1 empty item>, 'c', 'd', 'e' ]
[ 'a', <1 empty item>, 'c', <1 empty item>, 'e' ]
[ 'a', 'c', 'e' ]
condenseDeep
Makes all the arrays in the object non-sparse.
_.condenseDeep( obj, options = { checkCircular: false } ) => object
obj- The object/array to iterate over.optionscheckCircular(false) - Check each value to not be one of the parents, to avoid circular references.
returns- 'condensed' object/array without holes.
Example:
let obj = { arr: ['a', 'b', { c: [1, , 2, , 3] }, 'd', 'e'] };
delete obj.arr[1];
delete obj.arr[3];
_.condenseDeep(obj);
console.log(obj);
Console:
{ arr: [ 'a', { c: [ 1, 2, 3 ] }, 'e' ] }
eachDeep (forEachDeep)
Invokes given callback for each field and element of given object or array, nested too.
_.eachDeep( obj, iteratee=_.identity, options={
callbackAfterIterate: false,
checkCircular: false,
childrenPath: undefined,
includeRoot: !_.isArray(obj),
leavesOnly: false,
pathFormat: 'string',
rootIsChildren: !includeRoot && _.isArray(obj)
}) => object
obj- The object/array to iterate over.iteratee(_.identity) - The function invoked per iteration. Should returnfalseexplicitly to skip children of current node.optionscallbackAfterIterate(false) - invokeiterateetwice, before and after iteration over children. On second runcontextiteratee's argument will haveafterIterateflag set to thetrue. By default,iterateeinvoked before it's children only.checkCircular(false) - Check each value to not be one of the parents, to avoid circular references.leavesOnly(false) - Call iteratee for childless values only.pathFormat('string') - specifies'string'or'array'format of paths passed to the iteratee.includeRoot(!_.isArray(obj)) - treat given object as a valid part of the tree, so it will be passed into iteratee with undefined key/path/parent. By default true if obj is not array.childrenPath(undefined) - children collection's field name, path or array of any of this. Only elements of such collections will be passed into iteratee, if specified.rootIsChildren(!includeRoot && _.isArray(obj)) - treatobjas a top-level children collection, so its elements will be passed into iteratee without parent path check. Considered only ifchildrenPathspecified. By default true for arrays if notincludeRoot.
returns- source object
iteratee
a callback function which will be invoked for each child of the object.
(value, key, parentValue, context) => boolean
iteratee arguments
value- current field or element (or child only, if childrenPath specified)key|index- field name or array index of the valueparentValue- an object or an array which contains current valuecontext- an object with fields:path- path to the current valueparent- an object of the current parentvalue- value of the parent, equivalent ofparentValueargument.key- parent key|indexpath- parent pathparent- grandparent with the same structure.childrenPath- contains matchedchildrenPathpath of this parent node, chosen fromchildrenPatharray, if it was specified.
childrenPath- contains matchedchildrenPathpath of current value, chosen fromchildrenPatharray, if it was specified.parents- an array with all parent objects starting from the root level.parentobject described above is just the last element of this arrayobj- source objectdepth- current value's nesting levelafterIterate- this flag will be true if it's a second invocation of theiteratee. Seeoptions.callbackAfterIteratefor details.break- method to abort the iteration, no matter how deep is process currently. Works in eachDeep/forEachDeep only, not supported by filterDeep etc.
- next three fields are available if
options.checkCircularwastrue, otherwise they will beundefinedisCircular- true if the current value is a circular reference.circularParent- parent object fromparentsarray referenced by current value or null if notisCircular.circularParentIndex- index ofcircularParentin the parents array or-1
returns- returnfalseexplicitly to prevent iteration over current value's children
Example:
let circular = { a: { b: { c: {} } } };
circular.a.b.c = circular.a;
_.eachDeep(circular, (value, key, parent, ctx) => {
if (ctx.isCircular) {
console.log(
"Circular reference to "+ctx.circularParent.path+" skipped at " + ctx.path
);
return false; // explicit `false` will skip children of current value
}
//do your job here
},{ checkCircular: true });
Console:
Circular reference to a skipped at a.b.c
let children = [
{
name: 'grand 1',
children: [
{
name: 'parent 1.1',
children: [{ name: 'child 1.1.1' }, { name: 'child 1.1.2' }],
},
{
name: 'parent 1.2',
children: [{ name: 'child 1.2.1' }, { name: 'child 1.2.2' }],
},
],
},
{
name: 'grand 2',
children: [
{
name: 'parent 2.1',
children: [{ name: 'child 2.1.1' }, { name: 'child 2.1.2' }],
},
{
name: 'parent 2.2',
children: [{ name: 'child 2.2.1' }, { name: 'child 2.2.2' }],
},
],
},
];
let total = 0;
_.eachDeep(
children,
(child, i, parent, ctx) => {
console.log(_.repeat(' ', ctx.depth) + child.name);
total++;
},
{ childrenPath: 'children' }
);
console.log('total nodes: ' + total);
Console:
grand 1
parent 1.1
child 1.1.1
child 1.1.2
parent 1.2
child 1.2.1
child 1.2.2
grand 2
parent 2.1
child 2.1.1
child 2.1.2
parent 2.2
child 2.2.1
child 2.2.2
total nodes: 14
eachDeep method has no builtin way to stop the iteration.
When you return false - only children of the current value will be skipped.
To stop iteration as fast as possible you will need to continuously return false from the rest of callbacks.
let breakLoop = false;
_.eachDeep({ id: 1, children: [ {id: 2, children: [ { id: 3, children: []}]}]},
(v,k, parent, context) => {
if(breakLoop || v == 2) {
breakLoop = true;
return false;
}
console.log(k);
});
exists
Check if path exists in the object considering sparse arrays.
Unlike Lodash's has - exists returns false for empty array slots.
_.exists( obj, path ) => boolean
obj- object to inspectpath- path(string|array) to check for existensereturns-trueif path exists, otherwisefalse.
Example:
var obj = [,{a:[,'b']}];
_.exists(obj, 0); // false
_.exists(obj, 1); // true
_.exists(obj, '[1].a[0]'); // false
_.exists(obj, '[1].a[1]'); // true
filterDeep
Returns an object with childs of your choice only
_.filterDeep( obj, predicate, options={
checkCircular: false,
cloneDeep: _.cloneDeep,
condense: true,
keepCircular: true,
leavesOnly: childrenPath!==undefined,
pathFormat: 'string',
// replaceCircularBy: <value>,
includeRoot: !_.isArray(obj),
childrenPath: undefined,
rootIsChildren: !includeRoot && _.isArray(obj),
onTrue: {
skipChildren: true, // false if childrenPath
cloneDeep: true, // true if childrenPath
keepIfEmpty: true },
onUndefined: {
skipChildren: false, // false if childrenPath
cloneDeep: false, // true if childrenPath
keepIfEmpty: false },
onFalse: {
skipChildren: true, // false if childrenPath
cloneDeep: false, // true if childrenPath
keepIfEmpty: false },
}) => object
obj- The object/array to iterate over.predicate- The predicate is invoked with same arguments as described in iteratee subsection- If returns
true- it means this is good value and you want it in the result object. SeeonTrueoption for detailed behaviour description. - If returns
undefined- it means you don't know yet if you need this and will see if some children are good. Seeoptions.onUndefinedfor details. - If returns
false- current value will be completely excluded from the result object, iteration over children of this value will be skipped. Seeoptions.onFalseoption. - You can also return an object with
skipChildren,cloneDeepandkeepIfEmptyboolean fields to control the filtering process directly.
- If returns
optionscheckCircular(false) - Check each value to not be one of the parents, to avoid circular references.keepCircular(true) - The result object will contain circular references, if they passed the filter.replaceCircularBy(no defaults) - Specify the value to replace circular references by. Can beundefinedtoo.condense(true) - excluding some paths from the object may produce sparse arrays. By default result object will be deeply condensed, but if you need consistent source and result paths - you can switch it off.cloneDeep(_.cloneDeep)- Method to use for deep cloning values, Lodash cloneDeep by default.pathFormat('string') - specifies'string'or'array'format of paths passed to the iteratee.leavesOnly(options.childrenPath === undefined) - Call predicate for childless values only.includeRoot(!_.isArray(obj)) - treat given object as a valid part of the tree, so it will be passed into iteratee with undefined key/path/parent. By default true if obj is not array.childrenPath(undefined) - children collection's field name, path or array of any of this. Only elements of such collections will be passed into predicate, if specified.rootIsChildren(!includeRoot && _.isArray(obj)) - treatobjas a top-level children collection, so its elements will be passed into predicate without parent path check. Considered only ifchildrenPathspecified. By default true for arrays if notincludeRoot.onTrue(object) - Describes how current value should be processed if predicate returnstrueskipChildren(childrenPath===undefined) - if 'true' - skip iteration over value's children. By default true for 'object' mode and false in the 'tree' mode.cloneDeep(childrenPath!==undefined) - deeply clone current value into result or copy primitives only and create empty array/object without nested data. In the 'tree' mode whole child will be deeply cloned to the result.keepIfEmpty(true) - keep empty array/object in the result, if all the children were filtered out/not exist.
onUndefined(object) - Describes how current value should be processed if iteratee returnsundefinedskipChildren(false) - on undefined answer children will be still checked by defaultcloneDeep(childrenPath!==undefined) - copy only primitives for 'object' mode and cloneDeep for 'tree' mode. In the tree mode only children count considered to decide if value empty or not, other cloned fields doesn't matter.keepIfEmpty(false) - remove such value from result if no children passed the filter by default.
onFalse(object) - Describes how current value should be processed if iteratee returnsfalseskipChildren(childrenPath===undefined) - by default reject value completely in the 'object' mode, but give children a chance in the 'tree' modecloneDeep(childrenPath!==undefined) - no need to clone if we rejected value in 'object' mode, but in the 'tree' mode we will possibly need other fields of the value, if some children will be welcome.keepIfEmpty(false) - remove from result if no children passed the filter by default.
returns- deeply filtered object/array/any type of given source obj or null if everything was rejected.
Example(fields iteration):
let things = {
things: [
{ name: 'something', good: false },
{
name: 'another thing', good: true,
children: [
{ name: 'child thing 1', good: false },
{ name: 'child thing 2', good: true },
{ name: 'child thing 3', good: false },
],
},
{
name: 'something else', good: true,
subItem: { name: 'sub-item', good: false },
subItem2: { name: 'sub-item-2', good: true },
},
],
};
let filtrate = _.filterDeep(
things,
(value, key, parent) => {
if (key == 'name' && parent.good) return true;
if (key == 'good' && value == true) return true;
}
);
console.log(filtrate);
Console:
{ things:
[ { name: 'another thing',
good: true,
children: [ { name: 'child thing 2', good: true } ] },
{ name: 'something else',
good: true,
subItem2: { name: 'sub-item-2', good: true } } ] }
Example (tree mode)
let badChildren = [
{
name: '1',
bad: false,
children: [
{ name: '1.1', bad: false },
{ name: '1.2' },
{ name: '1.3', bad: true },
],
},
{
name: '2',
children: [
{ name: '2.1', bad: false },
{ name: '2.2' },
{ name: '2.3', bad: true },
],
},
{
name: '3',
bad: true,
children: [
{ name: '3.1', bad: false },
{ name: '3.2' },
{ name: '3.3', bad: true },
],
},
];
let reallyBad = _.filterDeep(badChildren, 'bad', { childrenPath: 'children' });
console.log(reallyBad);
Console:
[
{
"name": "1",
"bad": false,
"children": [
{
"name": "1.3",
"bad": true
}
]
},
{
"name": "2",
"children": [
{
"name": "2.3",
"bad": true
}
]
},
{
"name": "3",
"bad": true,
"children": [
{
"name": "3.3",
"bad": true
}
]
}
]
findDeep
Returns first matching deep meta-value
_.findDeep( obj, predicate, options={
checkCircular: false,
leavesOnly: childrenPath!==undefined,
pathFormat: 'string',
includeRoot: !_.isArray(obj),
childrenPath: undefined,
rootIsChildren: !includeRoot && _.isArray(obj),
}) => {value, key, parent, context}
obj- The object/array to iterate over.predicate- The predicate is invoked with same arguments as described in iteratee subsection- If returns
true- all the arguments passed into predicate will be returned as an object and search will be stopped.
- If returns
optionscheckCircular(false) - Check each value to not be one of the parents, to avoid circular references.pathFormat('string') - specifies'string'or'array'format of paths passed to the iteratee.leavesOnly(options.childrenPath === undefined) - Call predicate for childless values only.includeRoot(!_.isArray(obj)) - treat given object as a valid part of the tree, so it will be passed into iteratee with undefined key/path/parent. By default true if obj is not array.childrenPath(undefined) - children collection's field name, path or array of any of this. Only elements of such collections will be passed into predicate, if specified.rootIsChildren(!includeRoot && _.isArray(obj)) - treatobjas a top-level children collection, so its elements will be passed into predicate without parent path check. Considered only ifchildrenPathspecified. By default true for arrays if notincludeRoot.
returns- and object with found value, key, parent and context or undefined if nothing found
Try it yourself (no yet) ›››
findValueDeep
Returns first matching deep value.
_.findValueDeep( obj, predicate, options={
checkCircular: false,
leavesOnly: childrenPath!==undefined,
pathFormat: 'string',
includeRoot: !_.isArray(obj),
childrenPath: undefined,
rootIsChildren: !includeRoot && _.isArray(obj),
}) => value | undefined
obj- The object/array to iterate over.predicate- The predicate is invoked with same arguments as described in iteratee subsection- If returns
true- the value passed into predicate will be returned and search will be stopped.
- If returns
optionscheckCircular(false) - Check each value to not be one of the parents, to avoid circular references.pathFormat('string') - specifies'string'or'array'format of paths passed to the iteratee.leavesOnly(options.childrenPath === undefined) - Call predicate for childless values only.includeRoot(!_.isArray(obj)) - treat given object as a valid part of the tree, so it will be passed into iteratee with undefined key/path/parent. By default true if obj is not array.childrenPath(undefined) - children collection's field name, path or array of any of this. Only elements of such collections will be passed into predicate, if specified.rootIsChildren(!includeRoot && _.isArray(obj)) - treatobjas a top-level children collection, so its elements will be passed into predicate without parent path check. Considered only ifchildrenPathspecified. By default true for arrays if notincludeRoot.
returns- found value or undefined if nothing found. Be carefull, deep value may also be undefined
Try it yourself (no yet) ›››
findPathDeep
Returns the path of the first matching deep value.
_.findPathDeep( obj, predicate, options={
checkCircular: false,
leavesOnly: childrenPath!==undefined,
pathFormat: 'string',
includeRoot: !_.isArray(obj),
childrenPath: undefined,
rootIsChildren: !includeRoot && _.isArray(obj),
}) => path | undefined
obj- The object/array to iterate over.predicate- The predicate is invoked with same arguments as described in iteratee subsection- If returns
true- current path will be returned and search will be stopped.
- If returns
optionscheckCircular(false) - Check each value to not be one of the parents, to avoid circular references.pathFormat('string') - specifies'string'or'array'format of paths passed to the iteratee.leavesOnly(options.childrenPath === undefined) - Call predicate for childless values only.includeRoot(!_.isArray(obj)) - treat given object as a valid part of the tree, so it will be passed into iteratee with undefined key/path/parent. By default true if obj is not array.childrenPath(undefined) - children collection's field name, path or array of any of this. Only elements of such collections will be passed into predicate, if specified.rootIsChildren(!includeRoot && _.isArray(obj)) - treatobjas a top-level children collection, so its elements will be passed into predicate without parent path check. Considered only ifchildrenPathspecified. By default true for arrays if notincludeRoot.
returns- the path of the found value or undefined if nothing found. Be carefull, path may also be undefined for datasource object itself, if includeRoot == true
Try it yourself (no yet) ›››
index
Creates an 'index' flat object with paths as keys and corresponding values.
_.index( obj, options={
checkCircular: false,
includeCircularPath: true,
leavesOnly: true,
includeRoot: !_.isArray(obj),
childrenPath: undefined,
rootIsChildren: !includeRoot && _.isArray(obj),
}) => object
obj- The object to iterate over.optionscheckCircular(false) - Check each value to not be one of the parents, to avoid circular references.includeCircularPath(true) - If found some circular reference - include a path to it into the result or skip it. Option ignored ifcheckCircular=falseleavesOnly(true) - Return paths to childless values only.includeRoot(!_.isArray(obj)) - in theindexmethod this option affects onlyrootIsChildrendefault value.childrenPath(undefined) - children collection's field name, path or array of any of this. Only elements of such collections will be listed in the index object, if specified.rootIsChildren(!includeRoot && _.isArray(obj)) - treatobjas a top-level children collection, so its elements will be listed as children too. Considered only ifchildrenPathspecified. By default true for arrays if notincludeRoot.
returns- 'index' object
Example:
let index = _.index(
{
a: {
b: {
c: [1, 2, 3],
'hello world': {},
},
},
},
{ leavesOnly: true }
);
console.log(index);
Console:
{ 'a.b.c[0]': 1,
'a.b.c[1]': 2,
'a.b.c[2]': 3,
'a.b["hello world"]': {} }
paths (keysDeep)
Creates an array with all the paths to each nested value.
_.paths( obj, options={
checkCircular: false,
includeCircularPath: true,
pathFormat: 'string',
leavesOnly: true,
includeRoot: !_.isArray(obj),
childrenPath: undefined,
rootIsChildren: !includeRoot && _.isArray(obj)
}) => array
obj- The object to iterate over.optionscheckCircular(false) - Check each value to not be one of the parents, to avoid circular references.includeCircularPath(true) - If found some circular reference - include a path to it into the result or skip it. Option ignored ifcheckCircular:falsepathFormat('string') - specifies'string'or'array'format of paths passed to the iteratee.leavesOnly(true) - Return paths to childless values only.includeRoot(!_.isArray(obj)) - in thepathsmethod this option affects onlyrootIsChildrendefault value.childrenPath(undefined) - children collection's field name, path or array of any of this. Only paths to elements of such collections will be listed in the result array, if specified.rootIsChildren(!includeRoot && _.isArray(obj)) - treatobjas a top-level children collection, so its elements will be listed as children too. Considered only ifchildrenPathspecified. By default true for arrays if notincludeRoot.
returns- array with paths of the object, formatted as strings or as arrays
Example:
let paths = _.paths({
a: {
b: {
c: [1, 2, 3],
"hello world":{}
},
},
},{ leavesOnly: false });
console.log(paths);
paths = _.paths({
a: {
b: {
c: [1, 2, 3],
"hello world":{}
},
},
});
console.log(paths);
Console:
[ 'a',
'a.b',
'a.b.c',
'a.b.c[0]',
'a.b.c[1]',
'a.b.c[2]',
'a.b["hello world"]' ]
[
'a.b.c[0]',
'a.b.c[1]',
'a.b.c[2]',
'a.b["hello world"]' ]
mapDeep
returns an array of deep values processed by iteratee. previous implemetation with object structure preserved renamed to mapValuesDeep
_.mapDeep( obj, iteratee, options) => object
obj- The object/array to iterate over.iteratee(_.identity) - The function invoked per iteration with four arguments (see iteratee subsection for details)valuekey|indexparentValuecontextreturns- desired value instead of initial to be added to result array
options- (see eachDeep options for details)callbackAfterIterate(false)checkCircular(false)leavesOnly(false)pathFormat('string')includeRoot(!_.isArray(obj))childrenPath(undefined)rootIsChildren(!includeRoot && _.isArray(obj))
returns- array of deep values processed by iteratee.
Example:
let res = _.mapDeep(
{ hello: { from: { the: 'deep world', and: 'deepdash' } } },
(v) => v.toUpperCase(),
{ leavesOnly: true }
);
// res -> ['DEEP WORLD','DEEPDASH']
Try it yourself (no yet) ›››
mapValuesDeep
returns an object with the same structure with values trasformed thru iteratee. if some value changed type from/to array - children will be skipped and given value will be used as is
_.mapValuesDeep( obj, iteratee, options) => object
obj- The object/array to iterate over.iteratee(_.identity) - The function invoked per iteration with four arguments (see iteratee subsection for details)valuekey|indexparentValuecontextskipChildren(boolean)- use this method to override default skip children behavior. Note: children values will be placed by original paths even if parent changed type from / to array.
returns- desired value instead of initial to be set at the same path
options- (see eachDeep options for details)callbackAfterIterate(false)checkCircular(false)leavesOnly(false)pathFormat('string')includeRoot(!_.isArray(obj))childrenPath(undefined)rootIsChildren(!includeRoot && _.isArray(obj))
returns- object or array with the same paths, but transformed values.
Example:
let res = _.mapValuesDeep(
{ hello: { from: { the: 'deep world' } } },
(v) => v.toUpperCase(),
{ leavesOnly: true }
);
// res -> { hello: { from: { the: 'DEEP WORLD' } } }
mapKeysDeep
returns an object with the same values but kyes trasformed thru iteratee.
_.mapKeysDeep( obj, iteratee, options) => object
obj- The object/array to iterate over.iteratee(_.identity) - The function invoked per iteration with four arguments (see iteratee subsection for details)valuekey|indexparentValuecontextreturns- desired key instead of initial
options- (see eachDeep options for details)callbackAfterIterate(false)checkCircular(false)leavesOnly(false)pathFormat('string')includeRoot(!_.isArray(obj))childrenPath(undefined)rootIsChildren(!includeRoot && _.isArray(obj))
returns- object or array with the same values, but transformed keys.
Example:
let res = _.mapKeysDeep(
{ hello: { from: { the: 'deep world' } } },
(v, k) => k.toUpperCase()
);
// res -> { HELLO: { FROM: { THE: 'deep world' } } }
Try it yourself (no yet) ›››
pickDeep
returns an object only with given path endings or regexes
_.pickDeep( obj, paths, options={
checkCircular: false,
keepCircular: true,
// replaceCircularBy: <value>,
condense: true,
onMatch: {
cloneDeep: false,
skipChildren: false,
keepIfEmpty: true,
},
onNotMatch: {
cloneDeep: false,
skipChildren: false,
keepIfEmpty: false,
}
}) => object
obj- The object/array to pick from.paths- array or single path criteria to pick. Can be string or regex. In case if string every path will be tested if it's end equal to given criteria, key by key from the end.optionscheckCircular(false) - Check each value to not be one of the parents, to avoid circular references.keepCircular(true) - The result object will contain circular references if they passed the filter.replaceCircularBy(no defaults) - Specify the value to replace circular references by.condense(true) - Condense the result object, since excluding some paths may produce sparse arrays.onMatch(object) - describes how current value should be processed, if current path matches the criteria. By default it will be copied into result object without deep cloning, and all it's deeper children will be inspected.skipChildren(false) - skip or iterate over value's childrencloneDeep(false) - deeply clone current value into result or copy primitives only and create empty array/object without nested data.keepIfEmpty(true) - keep empty array/object in the result, if all the children were filtered out/not exist.
onNotMatch(object) - describes how current value should be processed, if current path NOT matches the criteria. By default it will be completely excluded from the result object and deeper children check will be skiped.cloneDeep(false)skipChildren(false)keepIfEmpty(false)
returns- object/array with picked values only
Example:
let obj = {
good1: true,
bad1: false,
good2: { good3: true, bad3: true },
bad2: { good: true },
good4: [{ good5: true, bad5: true }],
bad4: [],
};
let clean = _.pickDeep(obj, ['good', 'good1', 'good2', 'good3', 'good4', 'good5']);
console.log(clean);
clean = _.pickDeep(obj, /\.?good\d*$/);
console.log(clean);
Console(x2):
{ good1: true,
good2: { good3: true },
bad2: { good: true },
good4: [ { good5: true } ] }
omitDeep
returns an object without given path endings or regexes
_.omitDeep( obj, paths, options={
checkCircular: false,
keepCircular: true,
// replaceCircularBy: <value>,
condense: true,
onMatch: {
cloneDeep: false,
skipChildren: false,
keepIfEmpty: false,
},
onNotMatch: {
cloneDeep: false,
skipChildren: false,
keepIfEmpty: true,
}
}) => object
obj- The object to exclude from.paths- - array or single path criteria to omit. Can be string or regex. In case if string every path will be tested if it's end equal to given criteria, key by key from the end.optionscheckCircular(false) - Check each value to not be one of the parents, to avoid circular references.keepCircular(true) - The result object will contain circular references if they passed the filter.replaceCircularBy(no defaults) - Specify the value to replace circular references by.condense(true) - Condense the result object, since excluding some paths may produce sparse arraysonMatch(object) - describes how current value should be processed, if current path matches the criteria. By default it will be completely excluded from the result object and deeper children check will be skiped.skipChildren(false) - skip or iterate over value's childrencloneDeep(false) - deeply clone current value into result or copy primitives only and create empty array/object without nested data.keepIfEmpty(false) - keep empty array/object in the result, if all the children were filtered out/not exist.
onNotMatch(object) - describes how current value should be processed, if current path NOT matches the criteria. By default it will be copied into result object without deep cloning, and all it's deeper children will be inspected.cloneDeep(false)skipChildren(false)keepIfEmpty(true)
returns- object without specified values.
Example:
let obj = {
good1: true,
bad1: false,
good2: { good3: true, bad3: false },
bad2: { good: true },
good4: [{ good5: true, bad5: false }],
bad4: [],
};
var clean = _.omitDeep(obj, ['bad1', 'bad2', 'bad3', 'bad4', 'bad5']);
console.log(clean);
clean = _.omitDeep(obj, /\.?bad\d*$/);
console.log(clean);
Console:
{ good1: true,
good2: { good3: true },
bad2: { good: true },
good4: [{ good5: true }] }
reduceDeep
Reduces object to a value which is the accumulated result of running each nested property/element in the object thru iteratee, where each invocation is supplied the return value of the previous. If accumulator is not given, the first value will be used as the initial value and will not be passed into ieratee. The iteratee is invoked with five arguments: (accumulator, value, key, parentValue, context).
_.reduceDeep( obj, iteratee, accumulator, options) => object
obj- The object/array to iterate over.iteratee(_.identity) - The function invoked per iteration with five arguments (see iteratee subsection for details)accumulator- most recent returned iteratee result or initial value or first valuevaluekey|indexparentValuecontext
accumulator- initial accumulator value. The very first iterated value will be used if undefined. In this case such value will not be passed into iteratee.options- (see eachDeep options for details)callbackAfterIterate(false)checkCircular(false)leavesOnly(false)pathFormat('string')includeRoot(!_.isArray(obj))childrenPath(undefined)rootIsChildren(!includeRoot && _.isArray(obj))
returns- finalaccumulatorvalue
Example:
let max = _.reduceDeep({ a: 2, b: 3, c: { d: 6, e: [1, 5, 8] } },
(acc, value, key, parent, ctx) => {
if (typeof value == 'number' && (typeof acc != 'number' || value > acc))
return value;
return undefined;
}
);
// max == 8
someDeep
Returns true if some matching deep value found otherwise returns false.
_.someDeep( obj, predicate, options={
checkCircular: false,
leavesOnly: childrenPath!==undefined,
pathFormat: 'string',
includeRoot: !_.isArray(obj),
childrenPath: undefined,
rootIsChildren: !includeRoot && _.isArray(obj),
}) => boolean
obj- The object/array to iterate over.predicate- The predicate is invoked with same arguments as described in iteratee subsection- If returns
truefor some deep value - true will be returned by someDeep and search will be stopped.
- If returns
optionscheckCircular(false) - Check each value to not be one of the parents, to avoid circular references.pathFormat('string') - specifies'string'or'array'format of paths passed to the iteratee.leavesOnly(options.childrenPath === undefined) - Call predicate for childless values only.includeRoot(!_.isArray(obj)) - treat given object as a valid part of the tree, so it will be passed into iteratee with undefined key/path/parent. By default true if obj is not array.childrenPath(undefined) - children collection's field name, path or array of any of this. Only elements of such collections will be passed into predicate, if specified.rootIsChildren(!includeRoot && _.isArray(obj)) - treatobjas a top-level children collection, so its elements will be passed into predicate without parent path check. Considered only ifchildrenPathspecified. By default true for arrays if notincludeRoot.
returns- true if some deep value found or false if not.
Try it yourself (no yet) ›››
pathToString
Converts given path from array to string format.
_.pathToString( path, ...prefixes ) => string;
path- path in array format...prefixes- any number of string prefixes to prepend result path correctly (with or without dots)returns- path in string format
Example:
console.log(_.pathToString(['a', 'b', 'c', 'defg', 0, '1', 2.3]));
Console:
a.b.c.defg[0][1]["2.3"]
Other traversal methods
Feel free to request other methods implementation.