Deepdash
Looking for eachDeep, filterDeep, keysDeep etc? Tree traversal extension for Lodash.
Installation
In a browser load script after Lodash:
<script src="lodash.js"></script>
<script src="deepdash.js"></script>
Using npm:
npm i --save deepdash
In Node.js (same for the Angular component):
const _ = require('deepdash')(require('lodash'));
Usage
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, path, depth, parent, parentKey, parentPath) => {
console.log(
_.repeat(' ', depth) +
key +
':' +
(value === null ? 'null' : typeof value),
parentPath && ' @' + parentPath
);
if(key=="skip"){
return false;
}
});
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, path, depth, parent, parentKey, parentPath) => {}).value();
Tutorials
filterDeep,indexate and condenseDeep
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 returns false
for empty array slots - filterDeep - deep filter object
- indexate - get an object with all the paths as keys and corresponding values
- paths - (keysDeep) get an array of paths
- pathToString - convert an array to string path (opposite to _.toPath)
condense
Makes sparse array non-sparse. This method mutates object.
_.condense(
arr
);
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
Make all the arrays in the object non-sparse.
_.condenseDeep(
obj,
options = {
checkCircular: false,
}
);
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={
track: false,
pathFormat: 'string'
}
)
Example:
let circular = { a: { b: { c: {} } } };
circular.a.b.c = circular;
_.eachDeep(
circular,
(value, key, path, depth, parent, parentKey, parentPath, parents) => {
if (_.indexOf(parents.values, value) !== -1) {
console.log(
"Circular reference skipped for '" + key + "' at " + parentPath
);
return false;
}
}
,{track:true});
Console:
Circular reference skipped for 'c' at a.b
exists
Check if path exists in the object considering sparse arrays.
Alternative for Lodash has
method, which returns true for empty array slots.
_.exists(
obj,
path,
)
Example:
var obj = [,{a:[,'b']}];
_.exists(obj, 0);
_.exists(obj, 1);
_.exists(obj, '[1].a[0]');
_.exists(obj, '[1].a[1]');
filterDeep
Returns and object with childs of your choice only
_.filterDeep(
obj,
predicate,
options = {
checkCircular: false,
keepCircular: true,
leafsOnly: true,
condense: true,
cloneDeep: _.cloneDeep,
pathFormat: 'string',
}
)
Example:
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, path, depth, parent, parentKey, parentPath, parents) => {
if (key == 'name' && parent.good) return true;
if (key == 'good' && value == true) return true;
},
{ leafsOnly: 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 } } ] }
indexate
Creates an 'index' flat object with paths as keys and corresponding values.
_.indexate(
obj,
options={
checkCircular: false,
includeCircularPath: true,
leafsOnly: true
}
)
Example:
let index = _.indexate(
{
a: {
b: {
c: [1, 2, 3],
'hello world': {},
},
},
},
{ leafsOnly: 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 of the paths of object or array.
_.paths(
obj,
options = {
checkCircular: false,
includeCircularPath: true,
leafsOnly: true,
pathFormat: 'string',
}
)
Example:
let paths = _.paths({
a: {
b: {
c: [1, 2, 3],
"hello world":{}
},
},
},{ leafsOnly: 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"]' ]
pathToString
Converts given path from array to string format.
_.pathToString(
path,
);
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.