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
_.has
but returnsfalse
for 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.options
checkCircular
(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 returnfalse
explicitly to skip children of current node.options
callbackAfterIterate
(false) - invokeiteratee
twice, before and after iteration over children. On second runcontext
iteratee's argument will haveafterIterate
flag set to thetrue
. By default,iteratee
invoked 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)) - treatobj
as a top-level children collection, so its elements will be passed into iteratee without parent path check. Considered only ifchildrenPath
specified. 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 ofparentValue
argument.key
- parent key|indexpath
- parent pathparent
- grandparent with the same structure.childrenPath
- contains matchedchildrenPath
path of this parent node, chosen fromchildrenPath
array, if it was specified.
childrenPath
- contains matchedchildrenPath
path of current value, chosen fromchildrenPath
array, if it was specified.parents
- an array with all parent objects starting from the root level.parent
object 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.callbackAfterIterate
for 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.checkCircular
wastrue
, otherwise they will beundefined
isCircular
- true if the current value is a circular reference.circularParent
- parent object fromparents
array referenced by current value or null if notisCircular
.circularParentIndex
- index ofcircularParent
in the parents array or-1
returns
- returnfalse
explicitly 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
-true
if 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. SeeonTrue
option 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.onUndefined
for details. - If returns
false
- current value will be completely excluded from the result object, iteration over children of this value will be skipped. Seeoptions.onFalse
option. - You can also return an object with
skipChildren
,cloneDeep
andkeepIfEmpty
boolean fields to control the filtering process directly.
- If returns
options
checkCircular
(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 beundefined
too.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)) - treatobj
as a top-level children collection, so its elements will be passed into predicate without parent path check. Considered only ifchildrenPath
specified. By default true for arrays if notincludeRoot
.onTrue
(object) - Describes how current value should be processed if predicate returnstrue
skipChildren
(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 returnsundefined
skipChildren
(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 returnsfalse
skipChildren
(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
options
checkCircular
(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)) - treatobj
as a top-level children collection, so its elements will be passed into predicate without parent path check. Considered only ifchildrenPath
specified. 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
options
checkCircular
(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)) - treatobj
as a top-level children collection, so its elements will be passed into predicate without parent path check. Considered only ifchildrenPath
specified. 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
options
checkCircular
(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)) - treatobj
as a top-level children collection, so its elements will be passed into predicate without parent path check. Considered only ifchildrenPath
specified. 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.options
checkCircular
(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=false
leavesOnly
(true) - Return paths to childless values only.includeRoot
(!_.isArray(obj)) - in theindex
method this option affects onlyrootIsChildren
default 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)) - treatobj
as a top-level children collection, so its elements will be listed as children too. Considered only ifchildrenPath
specified. 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.options
checkCircular
(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:false
pathFormat
('string') - specifies'string'
or'array'
format of paths passed to the iteratee.leavesOnly
(true) - Return paths to childless values only.includeRoot
(!_.isArray(obj)) - in thepaths
method this option affects onlyrootIsChildren
default 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)) - treatobj
as a top-level children collection, so its elements will be listed as children too. Considered only ifchildrenPath
specified. 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)value
key|index
parentValue
context
returns
- 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)value
key|index
parentValue
context
skipChildren(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)value
key|index
parentValue
context
returns
- 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.options
checkCircular
(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.options
checkCircular
(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 valuevalue
key|index
parentValue
context
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
- finalaccumulator
value
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
true
for some deep value - true will be returned by someDeep and search will be stopped.
- If returns
options
checkCircular
(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)) - treatobj
as a top-level children collection, so its elements will be passed into predicate without parent path check. Considered only ifchildrenPath
specified. 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.