UNPKG

deepdash

Version:

➔ 𝐃eep standalone lib / 𝐋odash extension: ✓ eachDeep ✓ filterDeep ✓ mapDeep ✓ reduceDeep ✓ pickDeep ✓ omitDeep ✓ keysDeep ✓ index ✓ condenseDeep ⋮ Parents stack ⋮ Circular check ⋮ Leaves only mode ⋮ Children mode ⋮ cherry-pick ⋮ esm

469 lines (429 loc) 13.6 kB
# Deepdash > v1.9.5 Looking for eachDeep, filterDeep, omitDeep, keysDeep etc? Tree traversal extension for Lodash. > Deepdash lib is used in [PlanZed.org](https://planzed.org/) - awesome cloud mind map app created by the author of deepdash. Plz check it, it's free and I need [feedback](https://github.com/YuriGor/PlanZed.org) 😉 ## List of Methods - [condense](#condense) - condense sparse array - [condenseDeep](#condensedeep) - condense all the nested arrays - [eachDeep](#eachdeep-foreachdeep) - (forEachDeep) iterate over all the children and sub-children - [exists](#exists) - like a `_.has` but returns `false` for empty array slots - [filterDeep](#filterdeep) - deep filter object - [indexate](#indexate) - get an object with all the paths as keys and corresponding values - [omitDeep](#omitdeep) - get object without keys specified as string name or regex - [paths](#paths-keysdeep) - (keysDeep) get an array of paths - [pathToString](#pathtostring) - convert an array to string path (opposite to _.toPath) ### Installation In a browser load [script](https://cdn.jsdelivr.net/npm/deepdash/deepdash.min.js) after Lodash: ```html <script src="https://cdn.jsdelivr.net/npm/lodash/lodash.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/deepdash/deepdash.min.js"></script> ``` Using npm: ``` npm i --save deepdash ``` In Node.js (same for the Angular component): ```js //mixin new methods into Lodash object const _ = require('deepdash')(require('lodash')); ``` Or as [ECMAScript Module](https://nodejs.org/api/esm.html#esm_ecmascript_modules): ```js import lodash from "lodash"; import deepdash from "deepdash"; const _ = deepdash(lodash); ``` # Usage ```js 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; // 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: ```js _(obj).eachDeep((value, key, path, depth, parent, parentKey, parentPath) => {/* do */}).value(); ``` # Tutorials [filterDeep,indexate and condenseDeep](http://yurigor.com/deep-filter-js-object-or-array-with-lodash/) # Methods ## condense Makes sparse array non-sparse. This method mutates object. ```js _.condense( arr // array to condense ); ``` **Example:** ```js 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. ```js _.condenseDeep( obj, // The object to iterate over. options = { checkCircular: false, // Check each value to not be one of the parents, to avoid circular references. } ); ``` **Example:** ```js 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. ```js _.eachDeep( obj, // The object to iterate over iteratee=_.identity, // The function invoked per iteration. Return `false` explicitly to skip children of current node. options={ track: false, /* track parents from current back to the root, useful for circular reference detecting. If true, `iteratee` will have additional `parents` object argument with `values`, `keys` and `paths` arrays inside. */ pathFormat: 'string'/* 'string'|'array' - specifies the format of paths passed to the iteratee. 'array' is better for performance. 'string' is better for readability. */ } ) ``` **Example:** ```js 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; // if `false` returned explicitly, children of current `value` will be skipped. } //do your things } ,{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. ```js _.exists( obj, // object to inspect path, // path(string|array) to check for existense ) ``` **Example:** ```js 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 and object with childs of your choice only ```js _.filterDeep( obj, // The object to iterate over. predicate, /* The predicate is invoked with eight arguments: (value, key|index, path, depth, parent, parentKey, parentPath, parents) - If predicate returns `true` - value will be deeply cloned to the result object, no further iteration over children of this value will be performed. - If predicate returns `false` - value will be completely excluded from the result object, no further iteration over children of this value will be performed. - If predicate returns `undefined` - current path will only appear in the result object if some child elements will pass the filter during subsequent iterations or if keepUndefined=true. */ 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: <value>, // Specify the value to replace circular references by. leavesOnly: true, /* Call predicate for childless values only by default. Or, if set to false, all the intermediate objects will be passed into the predicate, including parents. */ condense: true, // Condense the result object, since excluding some paths may produce sparse arrays cloneDeep: _.cloneDeep, // Method to use for deep cloning values, Lodash cloneDeep by default. pathFormat: 'string', /* 'string'|'array' - specifies the format of paths passed to the iteratee. 'array' is better for performance. 'string' is better for readability. */ keepUndefined: false, /* keep field in the result object if iteratee returned undefined */ } ) ``` **Example:** ```js 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; }, { leavesOnly: 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. ```js _.indexate( 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 if `checkCircular:false`. */ leavesOnly: true /* Return paths to childless values only by default. Or all the paths will be returned, including parents, if set to false. */ } ) ``` **Example:** ```js let index = _.indexate( { 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"]': {} } ``` ## omitDeep returns an object without keys specified as string name or regex ```js _.omitDeep( obj, // The object to iterate over. key, // key or array of keys to exclude. Can be regex. 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: <value>, // Specify the value to replace circular references by. condense: true, // Condense the result object, since excluding some paths may produce sparse arrays } ) ``` **Example:** ```js 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(paths); clean = _.omitDeep(obj, /^bad.*$/); console.log(paths); ``` Console: ``` { good1: true, good2: { good3: true }, good4: [ { good5: true } ] } ``` ## paths (keysDeep) Creates an array of the paths of object or array. ```js _.paths( 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 if `checkCircular:false`. */ leavesOnly: true, /* Return paths to childless values only by default. Or all the paths will be returned, including parents, if set to false. */ pathFormat: 'string', /* 'string'|'array' - specifies the format of paths. 'array' is better for performance. 'string' is better for readability. */ } ) ``` **Example:** ```js 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"]' ] ``` ## pathToString Converts given path from array to string format. ```js _.pathToString( path, // path in array format ); ``` **Example:** ```js 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](https://github.com/YuriGor/deepdash/issues/new) other methods implementation.