Comparing version 2.1.3 to 3.1.0
# Change Log | ||
## unpublished | ||
**Breaking Changes** | ||
- `keepUndefined` option removed from the [filterDeep](/#filterdeep) method. Use onUndefined:{keepIfEmpty:true}, instead. | ||
- `keys` argument of the [omitDeep](/#omitdeep) and [pickDeep](/#pickdeep) methods became `paths` | ||
**Features added** | ||
- `tree` option added to the [filterDeep](/#filterdeep) method. | ||
- `tree` option added to the [indexate](/#indexate) method. | ||
- `tree` option added to the [paths (keysDeep)](/#paths-keysdeep) method. | ||
- `predicate` option of the [filterDeep](/#filterdeep) method is Lodash _.iteratee. | ||
- `cloneDeep` option of the [filterDeep](/#filterdeep) now can be set to `false`. | ||
- `callbackAfterIterate` option added to the [eachDeep](/#eachdeep-foreachdeep) method. | ||
- `onTrue` `onUndefined` and `onFalse` options added to the [filterDeep](/#filterdeep) method. `cloneDeep`, `keepIfEmpty`, `skipChildren` default values can be overwritten for each condition. | ||
- `onMatched` and `onNotMatched` options added to the [omitDeep](/#omitdeep) and [pickDeep](/#pickdeep) methods. | ||
- custom object replies from [filterDeep](/#filterdeep) iteratee, with `cloneDeep`, `keepIfEmpty`, `skipChildren` and `empty` fields now are supported. | ||
**Bugs fixed** | ||
- [filterDeep](/#filterdeep) method continues to iterate over object's children if it passed the filter. | ||
## v2-1-0 | ||
@@ -8,3 +31,3 @@ *(2019-03-02)* | ||
- `tree` option added to [eachDeep](/#eachdeep-foreachdeep) method. | ||
- `tree` option added to the [eachDeep](/#eachdeep-foreachdeep) method. | ||
Now it's much easier to iterate over tree with known `children` collection field name / path | ||
@@ -25,3 +48,3 @@ | ||
- checkCircular option added to [eachDeep](/#eachdeep-foreachdeep) method. | ||
- checkCircular option added to the [eachDeep](/#eachdeep-foreachdeep) method. | ||
- [pickDeep](/#peekdeep) method implemented. | ||
@@ -28,0 +51,0 @@ |
375
deepdash.js
@@ -6,2 +6,4 @@ 'use strict'; | ||
var rxVarName = /^[a-zA-Z_$]+([\w_$]*)$/; | ||
var errorNoLeavesOnlyAndTree = | ||
'"leavesOnly" option cannot be true in the "tree" mode.'; | ||
function pathToString(path) { | ||
@@ -28,2 +30,48 @@ if (_.isString(path)) return path; | ||
} | ||
function pathMatches(path, paths) { | ||
var pathString; | ||
var pathArray; | ||
if (_.isString(path)) { | ||
pathString = path; | ||
} else { | ||
pathArray = path; | ||
} | ||
if (!_.isArray(paths)) { | ||
paths = [paths]; | ||
} | ||
for (var i = 0; i < paths.length; i++) { | ||
if (_.isString(paths[i])) { | ||
paths[i] = _.toPath(paths[i]); | ||
} | ||
if (_.isArray(paths[i])) { | ||
if (pathArray === undefined) { | ||
pathArray = _.toPath(pathString); | ||
} | ||
if ( | ||
pathArray.length >= paths[i].length && | ||
_.isEqual(_.takeRight(pathArray, paths[i].length), paths[i]) | ||
) { | ||
// console.log('path matched'); | ||
return paths[i]; | ||
} | ||
} else if (paths[i] instanceof RegExp) { | ||
if (pathString === undefined) { | ||
pathString = pathToString(path); | ||
} | ||
if (paths[i].test(pathString)) { | ||
// console.log('regex matched', paths[i]); | ||
return paths[i]; | ||
} | ||
} else { | ||
throw new Error( | ||
'To match path use only string/regex or array of them.' | ||
); | ||
} | ||
} | ||
// console.log('matched nothing'); | ||
return false; | ||
} | ||
if (!_.pathMatches) { | ||
_.mixin({ pathMatches: pathMatches }, { chain: false }); | ||
} | ||
function iterate( | ||
@@ -53,3 +101,5 @@ value, | ||
parent: parent, | ||
isTreeNode: parent && parent.isTreeChildren, | ||
}; | ||
// console.log('it', currentObj.path); | ||
var currentParents = parents.concat(currentObj); | ||
@@ -66,6 +116,2 @@ if (options.tree) { | ||
} | ||
if (!_.isArray(options.tree.children)) { | ||
options.tree.children = [options.tree.children]; | ||
} | ||
// console.log('tree options', options.tree); | ||
if (!depth && options.tree.rootIsChildren) { | ||
@@ -76,32 +122,11 @@ currentObj.isTreeChildren = true; | ||
} else { | ||
for (var i = 0; i < options.tree.children.length; i++) { | ||
if (_.isString(options.tree.children[i])) { | ||
options.tree.children[i] = _.toPath(options.tree.children[i]); | ||
} | ||
if ( | ||
(_.isArray(options.tree.children[i]) && | ||
path.length >= options.tree.children[i].length && | ||
_.isEqual( | ||
_.takeRight(path, options.tree.children[i].length), | ||
options.tree.children[i] | ||
)) || | ||
(options.tree.children[i].test && | ||
options.tree.children[i].test( | ||
options.pathFormat == 'string' | ||
? currentObj.path | ||
: pathToString(path) | ||
)) | ||
) { | ||
currentObj.isTreeChildren = true; | ||
currentObj.treeChildrenPath = | ||
options.pathFormat == 'array' || | ||
!_.isArray(options.tree.children[i]) | ||
? options.tree.children[i] | ||
: pathToString(options.tree.children[i]); | ||
// console.log('✓ ' + options.tree.children[i], path); | ||
break; | ||
} else { | ||
currentObj.isTreeChildren = false; | ||
// console.log('× ' + options.tree.children[i], path); | ||
} | ||
var matches = pathMatches(path, options.tree.children); | ||
if (matches !== false) { | ||
currentObj.isTreeChildren = true; | ||
currentObj.treeChildrenPath = | ||
options.pathFormat == 'array' || !_.isArray(matches) | ||
? matches | ||
: pathToString(matches); | ||
} else { | ||
currentObj.isTreeChildren = false; | ||
} | ||
@@ -132,2 +157,5 @@ } | ||
isCircular = circularParentIndex !== -1; | ||
if (isCircular) { | ||
//console.log('cr: ', pathToString(childPath)); | ||
} | ||
if (options.tree) { | ||
@@ -140,16 +168,18 @@ // console.log( | ||
var res; | ||
var context = { | ||
path: | ||
options.pathFormat == 'array' ? childPath : pathToString(childPath), | ||
parent: currentObj, | ||
parents: currentParents, | ||
obj: obj, | ||
depth: depth, | ||
isCircular: isCircular, | ||
circularParent: circularParent, | ||
circularParentIndex: circularParentIndex, | ||
}; | ||
if (!options.tree || currentObj.isTreeChildren) { | ||
res = callback(childValue, childKey, value, { | ||
path: | ||
options.pathFormat == 'array' | ||
? childPath | ||
: pathToString(childPath), | ||
parent: currentObj, | ||
parents: currentParents, | ||
obj: obj, | ||
depth: depth, | ||
isCircular: isCircular, | ||
circularParent: circularParent, | ||
circularParentIndex: circularParentIndex, | ||
}); | ||
res = callback(childValue, childKey, value, context); | ||
// console.log('cb', context.path, currentObj.isTreeChildren); | ||
} else { | ||
// console.log('no cb', context.path, currentObj.isTreeChildren); | ||
} | ||
@@ -169,2 +199,9 @@ if (res !== false && !isCircular && _.isObject(value)) { | ||
} | ||
if ( | ||
options.callbackAfterIterate && | ||
(!options.tree || currentObj.isTreeChildren) | ||
) { | ||
context.afterIterate = true; | ||
callback(childValue, childKey, value, context); | ||
} | ||
}); | ||
@@ -197,9 +234,13 @@ } | ||
includeCircularPath: true, | ||
leavesOnly: true, | ||
leavesOnly: !options || !options.tree, | ||
}, | ||
options || {} | ||
); | ||
if (options.tree && options.leavesOnly) { | ||
throw new Error(errorNoLeavesOnlyAndTree); | ||
} | ||
var eachDeepOptions = { | ||
pathFormat: 'string', | ||
checkCircular: options.checkCircular, | ||
tree: options.tree, | ||
}; | ||
@@ -211,3 +252,7 @@ var res = {}; | ||
if (!context.isCircular || options.includeCircularPath) { | ||
if (options.leavesOnly && res[context.parent.path]) { | ||
if ( | ||
!options.tree && | ||
options.leavesOnly && | ||
res[context.parent.path] | ||
) { | ||
delete res[context.parent.path]; | ||
@@ -233,3 +278,3 @@ } | ||
includeCircularPath: true, | ||
leavesOnly: true, | ||
leavesOnly: !options || !options.tree, | ||
pathFormat: 'string', | ||
@@ -239,5 +284,9 @@ }, | ||
); | ||
if (options.tree && options.leavesOnly) { | ||
throw new Error(errorNoLeavesOnlyAndTree); | ||
} | ||
var eachDeepOptions = { | ||
pathFormat: options.pathFormat, | ||
checkCircular: options.checkCircular, | ||
tree: options.tree, | ||
}; | ||
@@ -249,3 +298,7 @@ var res = []; | ||
if (!context.isCircular || options.includeCircularPath) { | ||
if (options.leavesOnly && _.last(res) === context.parent.path) { | ||
if ( | ||
!options.tree && | ||
options.leavesOnly && | ||
_.last(res) === context.parent.path | ||
) { | ||
res.pop(); | ||
@@ -302,2 +355,3 @@ } | ||
var arrays = []; | ||
//console.log('condenseDeep → eachDeep'); | ||
_.eachDeep( | ||
@@ -320,5 +374,42 @@ obj, | ||
var filterDeep = function(obj, predicate, options) { | ||
predicate = _.iteratee(predicate); | ||
if (options && options.leafsOnly !== undefined) { | ||
options.leavesOnly = options.leafsOnly; | ||
} | ||
if (!options) { | ||
options = {}; | ||
} | ||
if (!options.onTrue) { | ||
options.onTrue = {}; | ||
} | ||
if (!options.onFalse) { | ||
options.onFalse = {}; | ||
} | ||
if (!options.onUndefined) { | ||
options.onUndefined = {}; | ||
} | ||
if (options.tree) { | ||
if (options.leavesOnly) { | ||
throw new Error(errorNoLeavesOnlyAndTree); | ||
} | ||
if (options.onTrue.skipChildren === undefined) { | ||
options.onTrue.skipChildren = false; | ||
} | ||
if (options.onUndefined.skipChildren === undefined) { | ||
options.onUndefined.skipChildren = false; | ||
} | ||
if (options.onFalse.skipChildren === undefined) { | ||
options.onFalse.skipChildren = false; | ||
} | ||
if (options.onTrue.cloneDeep === undefined) { | ||
options.onTrue.cloneDeep = true; | ||
} | ||
if (options.onUndefined.cloneDeep === undefined) { | ||
options.onUndefined.cloneDeep = true; | ||
} | ||
if (options.onFalse.cloneDeep === undefined) { | ||
options.onFalse.cloneDeep = true; | ||
} | ||
} | ||
options = _.merge( | ||
@@ -333,39 +424,108 @@ { | ||
pathFormat: 'string', | ||
keepUndefined: false, | ||
onTrue: { skipChildren: true, cloneDeep: true, keepIfEmpty: true }, | ||
onUndefined: { | ||
skipChildren: false, | ||
cloneDeep: false, | ||
keepIfEmpty: false, | ||
}, | ||
onFalse: { | ||
skipChildren: true, | ||
cloneDeep: false, | ||
keepIfEmpty: false, | ||
}, | ||
}, | ||
options || {} | ||
options | ||
); | ||
var eachDeepOptions = { | ||
pathFormat: options.pathFormat, | ||
checkCircular: options.checkCircular, | ||
tree: options.tree, | ||
callbackAfterIterate: true, | ||
}; | ||
var res = _.isArray(obj) ? [] : {}; | ||
var replies = {}; | ||
var foundCircular = []; | ||
//console.log('filterDeep → eachDeep'); | ||
_.eachDeep( | ||
obj, | ||
function(value, key, parent, context) { | ||
if (!context.isCircular) { | ||
if ( | ||
!_.isObject(value) || | ||
_.isEmpty(value) || | ||
!options.leavesOnly | ||
) { | ||
var condition = predicate(value, key, parent, context); | ||
if (condition === true) { | ||
_.set(res, context.path, options.cloneDeep(value)); | ||
} else if (condition === undefined && options.keepUndefined) { | ||
_.set( | ||
res, | ||
var curPath = pathToString(context.path); | ||
if (!context.afterIterate) { | ||
if (!context.isCircular) { | ||
// console.log('fr: ', context.path); | ||
var reply; | ||
if ( | ||
!options.leavesOnly || | ||
(context.parent && context.parent.isTreeChildren) || | ||
!_.isObject(value) || | ||
_.isEmpty(value) | ||
) { | ||
reply = predicate(value, key, parent, context); | ||
// console.log('?', reply); | ||
} | ||
if (!_.isObject(reply)) { | ||
if (reply === undefined) { | ||
reply = _.clone(options.onUndefined); | ||
} else if (reply) { | ||
reply = _.clone(options.onTrue); | ||
} else { | ||
reply = _.clone(options.onFalse); | ||
} | ||
} | ||
if (reply.empty === undefined) { | ||
reply.empty = true; | ||
} | ||
replies[curPath] = reply; | ||
// console.log('→', replies); | ||
if (reply.keepIfEmpty || !reply.skipChildren) { | ||
if (options.cloneDeep && reply.cloneDeep) { | ||
_.set(res, context.path, options.cloneDeep(value)); | ||
} else { | ||
_.set( | ||
res, | ||
context.path, | ||
_.isArray(value) | ||
? [] | ||
: _.isPlainObject(value) | ||
? {} | ||
: value | ||
); | ||
} | ||
} | ||
return !reply.skipChildren; | ||
} else { | ||
// console.log('fc: ', context.path); | ||
_.unset(res, context.path); | ||
if (options.keepCircular) { | ||
foundCircular.push([ | ||
context.path, | ||
_.isArray(value) ? [] : _.isPlainObject(value) ? {} : value | ||
); | ||
context.circularParent.path, | ||
]); | ||
} | ||
return false; | ||
} | ||
} else if (context.afterIterate && !context.isCircular) { | ||
// console.log('ai: ', context.path); | ||
if (replies[curPath].empty && !replies[curPath].keepIfEmpty) { | ||
_.unset(res, context.path); | ||
} else { | ||
var p = !options.tree | ||
? context.parent | ||
: _.findLast(context.parents, 'isTreeNode'); | ||
if (p) { | ||
p = pathToString(p.path); | ||
if (p) { | ||
// if (!replies[p]) | ||
// console.log('no reply for "' + p + '"', p); | ||
replies[p].empty = false; | ||
} | ||
} | ||
} | ||
delete replies[curPath]; | ||
return condition; | ||
} | ||
// console.log('←', replies); | ||
return; | ||
} else { | ||
if (options.keepCircular) { | ||
foundCircular.push([context.path, context.circularParent.path]); | ||
} | ||
return false; | ||
// console.log('aic: ', context.path); | ||
} | ||
@@ -377,4 +537,5 @@ }, | ||
var cv; | ||
var has = c[1] == '' || _.has(res, c[1]); | ||
if (!has) return; | ||
var exists = c[1] == '' || _.exists(res, c[1]); | ||
if (!exists) return; | ||
// console.log('circular: ', c[0], c[1]); | ||
if (_.has(options, 'replaceCircularBy')) { | ||
@@ -389,4 +550,6 @@ cv = options.replaceCircularBy; | ||
}); | ||
if (options.condense) | ||
if (options.condense) { | ||
//console.log('filterDeep → condenseDeep'); | ||
return _.condenseDeep(res, { checkCircular: options.checkCircular }); | ||
} | ||
return res; | ||
@@ -399,10 +562,5 @@ }; | ||
if (!_.omitDeep) { | ||
var omitDeep = function(obj, keys, options) { | ||
var omitDeep = function(obj, paths, options) { | ||
options = _.merge( | ||
{ | ||
checkCircular: false, | ||
keepCircular: true, | ||
//replaceCircularBy: <by>, | ||
condense: true, | ||
cloneDeep: _.cloneDeep, | ||
invert: false, | ||
@@ -412,24 +570,34 @@ }, | ||
); | ||
var isOmit = !options.invert; | ||
options = _.merge( | ||
{ | ||
onMatch: { | ||
cloneDeep: false, | ||
skipChildren: false, | ||
keepIfEmpty: !isOmit, | ||
}, | ||
onNotMatch: { | ||
cloneDeep: false, | ||
skipChildren: false, | ||
keepIfEmpty: isOmit, | ||
}, | ||
}, | ||
options | ||
); | ||
options.leavesOnly = false; | ||
options.tree = false; | ||
options.pathFormat = 'array'; | ||
options.keepUndefined = !options.invert; | ||
options.onTrue = options.invert ? options.onMatch : options.onNotMatch; | ||
options.onFalse = options.invert ? options.onNotMatch : options.onMatch; | ||
if (!_.isArray(keys)) keys = [keys]; | ||
keys = _.groupBy(keys, function(key) { | ||
return key instanceof RegExp ? 'regex' : 'const'; | ||
}); | ||
var test = function(value, key) { | ||
if (_.includes(keys.const, key)) { | ||
var test = function(value, key, parent, context) { | ||
if (pathMatches(context.path, paths) !== false) { | ||
// console.log('path match, return ', options.invert); | ||
return options.invert; | ||
} else { | ||
// console.log('path not match, return ', !options.invert); | ||
return !options.invert; | ||
} | ||
if ( | ||
_.some(keys.regex, function(rx) { | ||
return rx.test(key); | ||
}) | ||
) { | ||
return options.invert; | ||
} | ||
if (_.isObject(value) && _.size(value) !== 0) return undefined; | ||
return !options.invert; | ||
}; | ||
// console.log(options); | ||
return _.filterDeep(obj, test, options); | ||
@@ -441,6 +609,11 @@ }; | ||
if (!_.pickDeep) { | ||
var pickDeep = function(obj, keys, options) { | ||
options = options || {}; | ||
var pickDeep = function(obj, paths, options) { | ||
options = _.merge( | ||
{ | ||
invert: false, | ||
}, | ||
options || {} | ||
); | ||
options.invert = true; | ||
return _.omitDeep(obj, keys, options); | ||
return _.omitDeep(obj, paths, options); | ||
}; | ||
@@ -447,0 +620,0 @@ _.mixin({ pickDeep: pickDeep }); |
{ | ||
"name": "deepdash", | ||
"version": "2.1.3", | ||
"version": "3.1.0", | ||
"description": "➔ 𝐃eep extension for 𝐋odash: ✓ eachDeep ✓ filterDeep ✓ pickDeep ✓ omitDeep ✓ keysDeep ✓ indexate ✓ condenseDeep ⋮ Parent nodes tracking ⋮ Circular references check ⋮ Leaves only mode ⋮ Path as a valid js string or an array ⋮", | ||
@@ -14,3 +14,3 @@ "main": "deepdash.js", | ||
"coverage": "nyc report --reporter=text-lcov | coveralls", | ||
"dev": "nodemon ./node_modules/.bin/mocha --reporter min --no-deprecation" | ||
"dev": "nodemon ./node_modules/.bin/mocha -g 'pickDeep no mutation' --reporter min --no-deprecation" | ||
}, | ||
@@ -17,0 +17,0 @@ "repository": { |
715
README.md
@@ -9,15 +9,2 @@ <img src="deepdash.svg?sanitize=true" width="64px"/> | ||
## Methods | ||
(links to docs) | ||
- [condense](https://deepdash.io/#condense) - condense sparse array | ||
- [condenseDeep](https://deepdash.io/#condensedeep) - condense all the nested arrays | ||
- [eachDeep](https://deepdash.io/#eachdeep-foreachdeep) - (forEachDeep) iterate over all the children and sub-children | ||
- [exists](https://deepdash.io/#exists) - like a `_.has` but returns `false` for empty array slots | ||
- [filterDeep](https://deepdash.io/#filterdeep) - deep filter object | ||
- [indexate](https://deepdash.io/#indexate) - get an object with all the paths as keys and corresponding values | ||
- [pickDeep](https://deepdash.io/#pickdeep) - get object only with keys specified by names or regexes | ||
- [omitDeep](https://deepdash.io/#omitdeep) - get object without keys specified by names or regexes | ||
- [paths](https://deepdash.io/#paths-keysdeep) - (keysDeep) get an array of paths | ||
- [pathToString](https://deepdash.io/#pathtostring) - convert an array to string path (opposite to _.toPath) | ||
### Installation | ||
@@ -44,88 +31,642 @@ In a browser load [script](https://cdn.jsdelivr.net/npm/deepdash/deepdash.min.js) after Lodash: | ||
``` | ||
### Usage | ||
## Methods | ||
### eachDeep(forEachDeep) - iterate over all the children and sub-children (📚 [docs](https://deepdash.io/#eachdeep-foreachdeep)) | ||
<details> | ||
<summary>Example</summary> | ||
[children example collection is here](#example-collection) | ||
```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', | ||
let children = [/* ... */]; | ||
function displayField(val, key, parent, context) { | ||
if (_.isArray(parent)) { | ||
key = '[' + key + ']'; | ||
} | ||
console.log( | ||
_.repeat(' ', context.depth) + | ||
'→ ' + | ||
key + | ||
': ' + | ||
(_.isArray(val) | ||
? '[' + val.length + ']' | ||
: _.isObject(val) | ||
? '{' + (val.name || '') + '}' | ||
: val) | ||
); | ||
} | ||
console.log('\n = Iterate over tree (each child object) = \n'); | ||
_.eachDeep(children, displayField, { tree: true }); | ||
console.log('\n = Iterate over object (each field) = \n'); | ||
_.eachDeep(children, displayField); | ||
``` | ||
Console: | ||
``` | ||
= Iterate over tree (each child object) = | ||
→ [0]: {node 1} | ||
→ [0]: {node 1.1} | ||
→ [1]: {node 1.2} | ||
→ [2]: {node 1.3} | ||
→ [1]: {node 2} | ||
→ [0]: {node 2.1} | ||
→ [1]: {node 2.2} | ||
→ [2]: {node 2.3} | ||
→ [2]: {node 3} | ||
→ [0]: {node 3.1} | ||
→ [1]: {node 3.2} | ||
→ [2]: {node 3.3} | ||
= Iterate over object (each field) = | ||
→ [0]: {node 1} | ||
→ description: description for node 1 | ||
→ comment: comment for node 1 | ||
→ note: note for node 1 | ||
→ name: node 1 | ||
→ bad: false | ||
→ children: [3] | ||
→ [0]: {node 1.1} | ||
→ description: description for node 1.1 | ||
→ comment: comment for node 1.1 | ||
→ note: note for node 1.1 | ||
→ name: node 1.1 | ||
→ bad: false | ||
→ [1]: {node 1.2} | ||
→ description: description for node 1.2 | ||
→ comment: comment for node 1.2 | ||
→ note: note for node 1.2 | ||
→ name: node 1.2 | ||
→ good: true | ||
→ [2]: {node 1.3} | ||
→ description: description for node 1.3 | ||
→ comment: comment for node 1.3 | ||
→ note: note for node 1.3 | ||
→ name: node 1.3 | ||
→ bad: true | ||
→ good: false | ||
→ [1]: {node 2} | ||
→ description: description for node 2 | ||
→ comment: comment for node 2 | ||
→ note: note for node 2 | ||
→ name: node 2 | ||
→ good: true | ||
→ children: [3] | ||
→ [0]: {node 2.1} | ||
→ description: description for node 2.1 | ||
→ comment: comment for node 2.1 | ||
→ note: note for node 2.1 | ||
→ name: node 2.1 | ||
→ bad: false | ||
→ [1]: {node 2.2} | ||
→ description: description for node 2.2 | ||
→ comment: comment for node 2.2 | ||
→ note: note for node 2.2 | ||
→ name: node 2.2 | ||
→ good: true | ||
→ [2]: {node 2.3} | ||
→ description: description for node 2.3 | ||
→ comment: comment for node 2.3 | ||
→ note: note for node 2.3 | ||
→ name: node 2.3 | ||
→ bad: true | ||
→ good: false | ||
→ [2]: {node 3} | ||
→ description: description for node 3 | ||
→ comment: comment for node 3 | ||
→ note: note for node 3 | ||
→ name: node 3 | ||
→ bad: true | ||
→ good: false | ||
→ children: [3] | ||
→ [0]: {node 3.1} | ||
→ description: description for node 3.1 | ||
→ comment: comment for node 3.1 | ||
→ note: note for node 3.1 | ||
→ name: node 3.1 | ||
→ bad: false | ||
→ [1]: {node 3.2} | ||
→ description: description for node 3.2 | ||
→ comment: comment for node 3.2 | ||
→ note: note for node 3.2 | ||
→ name: node 3.2 | ||
→ good: true | ||
→ [2]: {node 3.3} | ||
→ description: description for node 3.3 | ||
→ comment: comment for node 3.3 | ||
→ note: note for node 3.3 | ||
→ name: node 3.3 | ||
→ bad: true | ||
→ good: false | ||
``` | ||
</details> | ||
### filterDeep - deep filter object (📚 [docs](https://deepdash.io/#filterdeep)) | ||
<details> | ||
<summary>Example</summary> | ||
[children example collection is here](#example-collection) | ||
```js | ||
let children = [/* ... */]; | ||
console.log('\n = Filter tree (good children) = \n'); | ||
console.log( | ||
_.filterDeep(children, 'good', { tree: true }) | ||
); | ||
console.log('\n = Filter object (names of good children) = \n'); | ||
console.log( | ||
_.filterDeep(children, (val, key, parent) => { | ||
if (key == 'name' && parent.good) return true; | ||
}) | ||
); | ||
``` | ||
Console: | ||
``` | ||
= Filter tree (good children) = | ||
[ | ||
{ | ||
"description": "description for node 1", | ||
"comment": "comment for node 1", | ||
"note": "note for node 1", | ||
"name": "node 1", | ||
"bad": false, | ||
"children": [ | ||
{ | ||
"description": "description for node 1.2", | ||
"comment": "comment for node 1.2", | ||
"note": "note for node 1.2", | ||
"name": "node 1.2", | ||
"good": true | ||
} | ||
] | ||
}, | ||
{ | ||
"description": "description for node 2", | ||
"comment": "comment for node 2", | ||
"note": "note for node 2", | ||
"name": "node 2", | ||
"good": true, | ||
"children": [ | ||
{ | ||
"description": "description for node 2.2", | ||
"comment": "comment for node 2.2", | ||
"note": "note for node 2.2", | ||
"name": "node 2.2", | ||
"good": true | ||
} | ||
] | ||
}, | ||
{ | ||
"description": "description for node 3", | ||
"comment": "comment for node 3", | ||
"note": "note for node 3", | ||
"name": "node 3", | ||
"bad": true, | ||
"good": false, | ||
"children": [ | ||
{ | ||
"description": "description for node 3.2", | ||
"comment": "comment for node 3.2", | ||
"note": "note for node 3.2", | ||
"name": "node 3.2", | ||
"good": true | ||
} | ||
] | ||
} | ||
] | ||
= Filter object (names of good children) = | ||
[ | ||
{ | ||
"children": [ | ||
{ | ||
"name": "node 1.2" | ||
} | ||
] | ||
}, | ||
{ | ||
"name": "node 2", | ||
"children": [ | ||
{ | ||
"name": "node 2.2" | ||
} | ||
] | ||
}, | ||
{ | ||
"children": [ | ||
{ | ||
"name": "node 3.2" | ||
} | ||
] | ||
} | ||
] | ||
``` | ||
</details> | ||
### pickDeep - get object only with keys specified by names or regexes (📚 [docs](https://deepdash.io/#pickdeep)) | ||
<details> | ||
<summary>Example</summary> | ||
[children example collection is here](#example-collection) | ||
```js | ||
let children = [/* ... */]; | ||
console.log('\n = Pick name and description only = \n'); | ||
console.log( | ||
_.pickDeep(children, ['name', 'description']) | ||
); | ||
``` | ||
Console: | ||
``` | ||
= Pick name and description only = | ||
[ | ||
{ | ||
"description": "description for node 1", | ||
"name": "node 1", | ||
"children": [ | ||
{ | ||
"description": "description for node 1.1", | ||
"name": "node 1.1" | ||
}, | ||
b: true, | ||
}, | ||
n: 12345, | ||
u: undefined, | ||
{ | ||
"description": "description for node 1.2", | ||
"name": "node 1.2" | ||
}, | ||
{ | ||
"description": "description for node 1.3", | ||
"name": "node 1.3" | ||
} | ||
] | ||
}, | ||
nl: null, | ||
}; | ||
_.eachDeep(obj, (value, key, parent, context) => { | ||
{ | ||
"description": "description for node 2", | ||
"name": "node 2", | ||
"children": [ | ||
{ | ||
"description": "description for node 2.1", | ||
"name": "node 2.1" | ||
}, | ||
{ | ||
"description": "description for node 2.2", | ||
"name": "node 2.2" | ||
}, | ||
{ | ||
"description": "description for node 2.3", | ||
"name": "node 2.3" | ||
} | ||
] | ||
}, | ||
{ | ||
"description": "description for node 3", | ||
"name": "node 3", | ||
"children": [ | ||
{ | ||
"description": "description for node 3.1", | ||
"name": "node 3.1" | ||
}, | ||
{ | ||
"description": "description for node 3.2", | ||
"name": "node 3.2" | ||
}, | ||
{ | ||
"description": "description for node 3.3", | ||
"name": "node 3.3" | ||
} | ||
] | ||
} | ||
] | ||
``` | ||
</details> | ||
### omitDeep - get object without keys specified by names or regexes (📚 [docs](https://deepdash.io/#omitdeep)) | ||
<details> | ||
<summary>Example</summary> | ||
[children example collection is here](#example-collection) | ||
```js | ||
let children = [/* ... */]; | ||
console.log('\n = Omit paths not ending with "e" = \n'); | ||
console.log( | ||
_.repeat(' ', context.depth) + | ||
key + | ||
':' + | ||
(value === null ? 'null' : typeof value), | ||
context.parent.path && ' @' + context.parent.path | ||
_.omitDeep(children, /[^e]$/i, { onMatch: { skipChildren: false } }), | ||
); | ||
if (key == 'skip') { | ||
return false; // return false explicitly to skip iteration over current value's children | ||
``` | ||
Console: | ||
``` | ||
= Omit paths not ending with "e" = | ||
[ | ||
{ | ||
"note": "note for node 1", | ||
"name": "node 1", | ||
"children": [ | ||
{ | ||
"note": "note for node 1.1", | ||
"name": "node 1.1" | ||
}, | ||
{ | ||
"note": "note for node 1.2", | ||
"name": "node 1.2" | ||
}, | ||
{ | ||
"note": "note for node 1.3", | ||
"name": "node 1.3" | ||
} | ||
] | ||
}, | ||
{ | ||
"note": "note for node 2", | ||
"name": "node 2", | ||
"children": [ | ||
{ | ||
"note": "note for node 2.1", | ||
"name": "node 2.1" | ||
}, | ||
{ | ||
"note": "note for node 2.2", | ||
"name": "node 2.2" | ||
}, | ||
{ | ||
"note": "note for node 2.3", | ||
"name": "node 2.3" | ||
} | ||
] | ||
}, | ||
{ | ||
"note": "note for node 3", | ||
"name": "node 3", | ||
"children": [ | ||
{ | ||
"note": "note for node 3.1", | ||
"name": "node 3.1" | ||
}, | ||
{ | ||
"note": "note for node 3.2", | ||
"name": "node 3.2" | ||
}, | ||
{ | ||
"note": "note for node 3.3", | ||
"name": "node 3.3" | ||
} | ||
] | ||
} | ||
}); | ||
] | ||
``` | ||
</details> | ||
### indexate - get an object with all the paths as keys and corresponding values (📚 [docs](https://deepdash.io/#indexate)) | ||
<details> | ||
<summary>Example</summary> | ||
```js | ||
let index = _.indexate( | ||
{ | ||
a: { | ||
b: { | ||
c: [1, 2, 3], | ||
'hello world': {}, | ||
}, | ||
}, | ||
}, | ||
{ leavesOnly: true } | ||
); | ||
console.log(index); | ||
``` | ||
Console: | ||
```json | ||
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: | ||
{ 'a.b.c[0]': 1, | ||
'a.b.c[1]': 2, | ||
'a.b.c[2]': 3, | ||
'a.b["hello world"]': {} } | ||
``` | ||
</details> | ||
### paths(keysDeep) - get an array of paths (📚 [docs](https://deepdash.io/#paths-keysdeep)) | ||
<details> | ||
<summary>Example</summary> | ||
```js | ||
_(obj).eachDeep((value, key, parent, context) => {/* do */}).value(); | ||
let paths = _.paths( | ||
{ | ||
a: { | ||
b: { | ||
c: [1, 2, 3], | ||
'hello world': {}, | ||
}, | ||
}, | ||
}, | ||
{ leavesOnly: false } | ||
); | ||
console.log(paths); | ||
``` | ||
### See [docs](https://deepdash.io) for details. | ||
Console: | ||
``` | ||
[ 'a', | ||
'a.b', | ||
'a.b.c', | ||
'a.b.c[0]', | ||
'a.b.c[1]', | ||
'a.b.c[2]', | ||
'a.b["hello world"]' ] | ||
``` | ||
</details> | ||
### condense - condense sparse array (📚 [docs](https://deepdash.io/#condense)) | ||
<details> | ||
<summary>Example</summary> | ||
```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' ] | ||
``` | ||
</details> | ||
### condenseDeep - condense all the nested arrays (📚 [docs](https://deepdash.io/#condensedeep)) | ||
<details> | ||
<summary>Example</summary> | ||
```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' ] } | ||
``` | ||
</details> | ||
### exists - like a `_.has` but returns `false` for empty array slots (📚 [docs](https://deepdash.io/#exists)) | ||
<details> | ||
<summary>Example</summary> | ||
```js | ||
var obj = [, { a: [, 'b'] }]; | ||
console.log(_.exists(obj, 0)); // false | ||
console.log(_.exists(obj, 1)); // true | ||
console.log(_.exists(obj, '[1].a[0]')); // false | ||
console.log(_.exists(obj, '[1].a[1]')); // true | ||
``` | ||
</details> | ||
### pathToString - convert an array to string path (opposite to _.toPath) (📚 [docs](https://deepdash.io/#pathtostring)) | ||
<details> | ||
<summary>Example</summary> | ||
```js | ||
console.log(_.pathToString(['a', 'b', 'c', 'defg', 0, '1', 2.3])); | ||
// a.b.c.defg[0][1]["2.3"] | ||
console.log(_.pathToString(['"', '"', '"'])); | ||
// ["\\""]["\\""]["\\""] | ||
console.log(_.pathToString('it.s.a.string')); | ||
// it.s.a.string | ||
``` | ||
</details> | ||
#### Example Collection | ||
<details> | ||
<summary>'children' object used in examples above</summary> | ||
```js | ||
let children = [ | ||
{ | ||
description: 'description for node 1', | ||
comment: 'comment for node 1', | ||
note: 'note for node 1', | ||
name: 'node 1', | ||
bad: false, | ||
children: [ | ||
{ | ||
description: 'description for node 1.1', | ||
comment: 'comment for node 1.1', | ||
note: 'note for node 1.1', | ||
name: 'node 1.1', | ||
bad: false, | ||
}, | ||
{ | ||
description: 'description for node 1.2', | ||
comment: 'comment for node 1.2', | ||
note: 'note for node 1.2', | ||
name: 'node 1.2', | ||
good: true, | ||
}, | ||
{ | ||
description: 'description for node 1.3', | ||
comment: 'comment for node 1.3', | ||
note: 'note for node 1.3', | ||
name: 'node 1.3', | ||
bad: true, | ||
good: false, | ||
}, | ||
], | ||
}, | ||
{ | ||
description: 'description for node 2', | ||
comment: 'comment for node 2', | ||
note: 'note for node 2', | ||
name: 'node 2', | ||
good: true, | ||
children: [ | ||
{ | ||
description: 'description for node 2.1', | ||
comment: 'comment for node 2.1', | ||
note: 'note for node 2.1', | ||
name: 'node 2.1', | ||
bad: false, | ||
}, | ||
{ | ||
description: 'description for node 2.2', | ||
comment: 'comment for node 2.2', | ||
note: 'note for node 2.2', | ||
name: 'node 2.2', | ||
good: true, | ||
}, | ||
{ | ||
description: 'description for node 2.3', | ||
comment: 'comment for node 2.3', | ||
note: 'note for node 2.3', | ||
name: 'node 2.3', | ||
bad: true, | ||
good: false, | ||
}, | ||
], | ||
}, | ||
{ | ||
description: 'description for node 3', | ||
comment: 'comment for node 3', | ||
note: 'note for node 3', | ||
name: 'node 3', | ||
bad: true, | ||
good: false, | ||
children: [ | ||
{ | ||
description: 'description for node 3.1', | ||
comment: 'comment for node 3.1', | ||
note: 'note for node 3.1', | ||
name: 'node 3.1', | ||
bad: false, | ||
}, | ||
{ | ||
description: 'description for node 3.2', | ||
comment: 'comment for node 3.2', | ||
note: 'note for node 3.2', | ||
name: 'node 3.2', | ||
good: true, | ||
}, | ||
{ | ||
description: 'description for node 3.3', | ||
comment: 'comment for node 3.3', | ||
note: 'note for node 3.3', | ||
name: 'node 3.3', | ||
bad: true, | ||
good: false, | ||
}, | ||
], | ||
}, | ||
]; | ||
``` | ||
</details> | ||
### See [full docs](https://deepdash.io) for details. |
@@ -39,2 +39,10 @@ 'use strict'; | ||
}); | ||
it('Completely filtered out', () => { | ||
let obj = { a: { b: undefined } }; | ||
let filtrate = _.filterDeep(obj, isNS, { | ||
leavesOnly: true, | ||
}); | ||
//console.log(filtrate); | ||
expect(filtrate).to.deep.equal({}); | ||
}); | ||
it('Count paths circular', () => { | ||
@@ -89,7 +97,29 @@ let filtrate = _.filterDeep(circular, isNS, { | ||
leavesOnly: false, | ||
cloneDeep: (o) => (_.isArray(o) ? [] : _.isObject ? {} : o), | ||
onTrue: { skipChildren: false }, | ||
}) | ||
.paths({ leavesOnly: false }) | ||
.omitDeep('o.d', { onMatch: {} }) | ||
.value(); | ||
expect(_.size(filtrate)).equal(18); | ||
// console.log(JSON.stringify(filtrate)); | ||
expect(filtrate).to.deep.equal({ | ||
a: { | ||
b: { | ||
c: { | ||
d: [ | ||
{}, | ||
{}, | ||
{}, | ||
{}, | ||
{}, | ||
{}, | ||
{ | ||
o: { | ||
f: {}, | ||
skip: { please: { dont: { go: {} } } }, | ||
}, | ||
}, | ||
], | ||
}, | ||
}, | ||
}, | ||
}); | ||
}); | ||
@@ -117,10 +147,9 @@ it('Circular', () => { | ||
}); | ||
// console.log(circular); | ||
//console.log(filtrate); | ||
filtrate = _.paths(filtrate, { | ||
leavesOnly: true, | ||
checkCircular: true, | ||
}); | ||
// console.log(filtrate); | ||
expect(_.size(filtrate)).equal(8); | ||
let err; | ||
try { | ||
JSON.stringify(filtrate); | ||
} catch (exc) { | ||
err = exc; | ||
} | ||
expect(err).equal(undefined); | ||
}); | ||
@@ -149,3 +178,4 @@ it('Not matched circular ', () => { | ||
}); | ||
filtrate.should.deep.equal({ a: [, , { b: true }] }); | ||
// console.log(filtrate); | ||
filtrate.should.deep.equal({ a: [, , { b: true }, ,] }); | ||
}); | ||
@@ -175,94 +205,3 @@ it('replaceCircularBy', () => { | ||
}); | ||
it('array path format', () => { | ||
const input = [ | ||
{ | ||
value: 'Miss1', | ||
children: [ | ||
{ value: 'Miss2' }, | ||
{ value: 'Hit1', children: [{ value: 'Miss3' }] }, | ||
], | ||
}, | ||
{ | ||
value: 'Miss4', | ||
children: [ | ||
{ value: 'Miss5' }, | ||
{ value: 'Miss6', children: [{ value: 'Hit2' }] }, | ||
], | ||
}, | ||
{ | ||
value: 'Miss7', | ||
children: [ | ||
{ value: 'Miss8' }, | ||
{ value: 'Miss9', children: [{ value: 'Miss10' }] }, | ||
], | ||
}, | ||
{ | ||
value: 'Hit3', | ||
children: [ | ||
{ value: 'Miss11' }, | ||
{ value: 'Miss12', children: [{ value: 'Miss13' }] }, | ||
], | ||
}, | ||
{ | ||
value: 'Miss14', | ||
children: [ | ||
{ value: 'Hit4' }, | ||
{ value: 'Miss15', children: [{ value: 'Miss16' }] }, | ||
], | ||
}, | ||
]; | ||
var keyword = 'Hit'; | ||
// We will need 2 passes, first - to collect needed nodes with 'Hit' value: | ||
var foundHit = _.filterDeep( | ||
input, | ||
function(value, key, parent, ctx) { | ||
expect(ctx.path).to.be.an.array(); | ||
expect(ctx.parent.path).to.be.an.array(); | ||
if (value.value && value.value.includes(keyword)) return true; | ||
}, | ||
{ | ||
condense: false, // keep empty slots in array to preserve correct paths | ||
leavesOnly: false, | ||
pathFormat: 'array', | ||
} | ||
); | ||
// second pass - to add missed fields both for found 'Hit' nodes and their parents. | ||
var filtrate = _.filterDeep( | ||
input, | ||
function(value, key, parent, ctx) { | ||
expect(ctx.path).to.be.an.array(); | ||
expect(ctx.parent.path).to.be.an.array(); | ||
if ( | ||
_.get(foundHit, ctx.path) !== undefined || | ||
_.get(foundHit, ctx.parent.path) !== undefined | ||
) { | ||
return true; | ||
} | ||
}, | ||
{ | ||
pathFormat: 'array', | ||
} | ||
); | ||
expect(filtrate).to.deep.equal([ | ||
{ | ||
value: 'Miss1', | ||
children: [{ value: 'Hit1', children: [{ value: 'Miss3' }] }], | ||
}, | ||
{ | ||
value: 'Miss4', | ||
children: [{ value: 'Miss6', children: [{ value: 'Hit2' }] }], | ||
}, | ||
{ | ||
value: 'Hit3', | ||
children: [ | ||
{ value: 'Miss11' }, | ||
{ value: 'Miss12', children: [{ value: 'Miss13' }] }, | ||
], | ||
}, | ||
{ | ||
value: 'Miss14', | ||
children: [{ value: 'Hit4' }], | ||
}, | ||
]); | ||
}); | ||
it('keepUndefined', () => { | ||
@@ -278,3 +217,3 @@ let filtrate = _.filterDeep( | ||
leavesOnly: false, | ||
keepUndefined: true, | ||
onUndefined: { keepIfEmpty: true }, | ||
} | ||
@@ -310,2 +249,64 @@ ); | ||
}); | ||
it('Custom reply', () => { | ||
let filtrate = _.filterDeep(demo, (v) => { | ||
if (v instanceof Date) { | ||
return { | ||
cloneDeep: false, | ||
keepIfEmpty: false, | ||
skipChildren: true, | ||
empty: true, | ||
}; | ||
} | ||
let t = typeof v; | ||
if (t == 'object') { | ||
return { cloneDeep: false, keepIfEmpty: true }; | ||
} | ||
if (t == 'string' || t == 'number') return true; | ||
}); | ||
// console.log(JSON.stringify(filtrate, null, 2)); | ||
expect(filtrate).to.deep.equal({ | ||
a: { | ||
b: { | ||
c: { | ||
d: [ | ||
{ | ||
i: 0, | ||
}, | ||
{ | ||
i: 1, | ||
}, | ||
{ | ||
i: 2, | ||
}, | ||
{ | ||
i: 3, | ||
}, | ||
{ | ||
i: 4, | ||
}, | ||
{ | ||
i: 5, | ||
}, | ||
{ | ||
o: { | ||
skip: { | ||
please: { | ||
dont: { | ||
go: { | ||
here: 'skip it', | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
], | ||
s: 'hello', | ||
}, | ||
}, | ||
n: 12345, | ||
}, | ||
nl: null, | ||
}); | ||
}); | ||
}); |
@@ -12,3 +12,3 @@ 'use strict'; | ||
var { demo, circular } = require('./object'); | ||
var { demo, circular, children } = require('./object'); | ||
@@ -73,5 +73,5 @@ describe('indexate', () => { | ||
}); | ||
it('Leafs only', () => { | ||
let index = _.indexate(demo, { leavesOnly: true }); | ||
// console.log(index); | ||
expect(_.size(index)).equal(14); | ||
@@ -103,2 +103,35 @@ }); | ||
}); | ||
it('No leavesOnly in tree', () => { | ||
try { | ||
_.indexate(children, { tree: true, leavesOnly: true }); | ||
} catch (exc) { | ||
expect(exc.message).equal( | ||
'"leavesOnly" option cannot be true in the "tree" mode.' | ||
); | ||
} | ||
}); | ||
it('Indexate tree', () => { | ||
let index = _.indexate(children, { tree: true }); | ||
let names = _(index) | ||
.values() | ||
.map('name') | ||
.value(); | ||
// console.log(names); | ||
expect(names).to.deep.equal([ | ||
'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', | ||
]); | ||
}); | ||
}); |
@@ -41,13 +41,2 @@ 'use strict'; | ||
}, | ||
circular: { | ||
a: { | ||
b: { | ||
c: { | ||
// e: circular.a.b, | ||
hi: 'planet', | ||
}, | ||
}, | ||
}, | ||
i: [1, 2, 3, 4, { hello: 'world' }], | ||
}, | ||
children: [ | ||
@@ -178,2 +167,89 @@ { | ||
], | ||
verifiedComments: [ | ||
{ | ||
name: 'Bob', | ||
text: 'Perfect!', | ||
rating: 5, | ||
verified: true, | ||
replies: [ | ||
{ | ||
name: 'admin', | ||
text: 'Thank you!', | ||
verified: true, | ||
replies: [ | ||
{ | ||
name: 'Bob', | ||
text: 'Write more!', | ||
verified: true, | ||
replies: [ | ||
{ | ||
name: 'admin', | ||
text: 'Ok :)', | ||
verified: true, | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
{ | ||
name: 'Augusta', | ||
text: 'like a brillaint!11', | ||
verified: true, | ||
}, | ||
], | ||
}, | ||
{ | ||
name: 'mr.John', | ||
text: 'Well done..', | ||
rating: 4, | ||
verified: false, | ||
replies: [ | ||
{ | ||
name: 'admin', | ||
text: 'Can it be better?', | ||
verified: true, | ||
replies: [ | ||
{ | ||
name: 'mr.John', | ||
text: 'May be last three lines can be shorter..', | ||
verified: false, | ||
replies: [ | ||
{ | ||
name: 'Bob', | ||
verified: true, | ||
text: "Don't listen to him, it will be unreadable!", | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
{ | ||
name: 'Mark', | ||
rating: 5, | ||
text: 'Any way to donate?', | ||
verified: false, | ||
replies: [ | ||
{ | ||
name: 'Larry', | ||
text: '+1', | ||
verified: true, | ||
}, | ||
], | ||
}, | ||
{ | ||
name: 'Regina', | ||
rating: 2, | ||
text: 'Not really like it', | ||
verified: true, | ||
replies: [ | ||
{ | ||
name: 'admin', | ||
text: ':(', | ||
verified: true, | ||
}, | ||
], | ||
}, | ||
], | ||
deeperComments: [ | ||
@@ -343,4 +419,56 @@ { | ||
], | ||
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 }, | ||
], | ||
}, | ||
], | ||
}; | ||
module.exports.circular = { | ||
a: { | ||
b: { | ||
c: { | ||
// e: circular.a.b, | ||
hi: 'planet', | ||
}, | ||
}, | ||
}, | ||
i: [ | ||
1, | ||
2, | ||
3, | ||
4, | ||
{ | ||
hello: 'world', | ||
} /* | ||
,[ | ||
circular | ||
,[circular.a] | ||
,{j:{ hello: 'world' } | ||
,{k:circular}}]*/, | ||
], | ||
}; | ||
module.exports.circular.a.b.c.e = module.exports.circular.a.b; | ||
@@ -351,3 +479,3 @@ module.exports.circular.i.push([ | ||
{ j: module.exports.circular.i[4] }, | ||
{ k: module.exports.circular.i[5] }, | ||
{ k: undefined }, | ||
]); | ||
@@ -354,0 +482,0 @@ |
@@ -19,3 +19,3 @@ 'use strict'; | ||
let clean = _.omitDeep(obj, 'skip'); | ||
clean = _.omitDeep(clean, 'o'); | ||
clean = _.omitDeep(clean, 'o', { onMatch: { skipChildren: true } }); | ||
expect(clean).to.deep.equal({ | ||
@@ -39,3 +39,3 @@ a: { | ||
let clean = _.omitDeep(demo, 'skip'); | ||
clean = _.omitDeep(clean, 'o'); | ||
clean = _.omitDeep(clean, 'o', { onMatch: { skipChildren: true } }); | ||
expect(clean).to.deep.equal({ | ||
@@ -65,3 +65,5 @@ a: { | ||
}; | ||
let clean = _.omitDeep(obj, ['bad1', 'bad2', 'bad3', 'bad4', 'bad5']); | ||
let clean = _.omitDeep(obj, ['bad1', 'bad2', 'bad3', 'bad4', 'bad5'], { | ||
onMatch: { skipChildren: true }, | ||
}); | ||
expect(clean).to.deep.equal({ | ||
@@ -82,3 +84,3 @@ good1: true, | ||
}; | ||
let clean = _.omitDeep(obj, /^bad.*$/); | ||
let clean = _.omitDeep(obj, /\.?bad.*$/); | ||
expect(clean).to.deep.equal({ | ||
@@ -85,0 +87,0 @@ good1: true, |
@@ -12,3 +12,3 @@ 'use strict'; | ||
var { demo, circular } = require('./object'); | ||
var { demo, circular, children } = require('./object'); | ||
@@ -162,2 +162,33 @@ describe('paths', () => { | ||
}); | ||
it('No leavesOnly in tree', () => { | ||
try { | ||
_.paths(children, { tree: true, leavesOnly: true }); | ||
} catch (exc) { | ||
expect(exc.message).equal( | ||
'"leavesOnly" option cannot be true in the "tree" mode.' | ||
); | ||
} | ||
}); | ||
it('paths of tree', () => { | ||
let paths = _.paths(children, { tree: true }); | ||
// console.log(paths); | ||
expect(paths).to.deep.equal([ | ||
'[0]', | ||
'[0].children[0]', | ||
'[0].children[0].children[0]', | ||
'[0].children[0].children[1]', | ||
'[0].children[1]', | ||
'[0].children[1].children[0]', | ||
'[0].children[1].children[1]', | ||
'[1]', | ||
'[1].children[0]', | ||
'[1].children[0].children[0]', | ||
'[1].children[0].children[1]', | ||
'[1].children[1]', | ||
'[1].children[1].children[0]', | ||
'[1].children[1].children[1]', | ||
]); | ||
}); | ||
}); |
@@ -15,6 +15,8 @@ 'use strict'; | ||
describe('pickDeep', () => { | ||
it('no mutation', () => { | ||
it('pickDeep no mutation', () => { | ||
let orig = _.cloneDeep(demo); | ||
let obj = _.cloneDeep(demo); | ||
let clean = _.pickDeep(obj, 'skip'); | ||
let clean = _.pickDeep(obj, 'skip', { | ||
onMatch: { cloneDeep: true, skipChildren: true }, | ||
}); | ||
expect(clean).to.deep.equal({ | ||
@@ -46,3 +48,5 @@ a: { | ||
it('demo skip', () => { | ||
let clean = _.pickDeep(demo, 'skip'); | ||
let clean = _.pickDeep(demo, 'skip', { | ||
onMatch: { skipChildren: true, cloneDeep: true }, | ||
}); | ||
@@ -82,10 +86,7 @@ expect(clean).to.deep.equal({ | ||
}; | ||
let clean = _.pickDeep(obj, [ | ||
'good1', | ||
'good2', | ||
'good3', | ||
'good', | ||
'good4', | ||
'good5', | ||
]); | ||
let clean = _.pickDeep( | ||
obj, | ||
['good1', 'good2', 'good3', 'good', 'good4', 'good5'], | ||
{ onMatch: { cloneDeep: true, skipChildren: true } } | ||
); | ||
expect(clean).to.deep.equal({ | ||
@@ -107,3 +108,3 @@ good1: true, | ||
}; | ||
let clean = _.pickDeep(obj, /^good.*$/); | ||
let clean = _.pickDeep(obj, /\.?good.*$/); | ||
expect(clean).to.deep.equal({ | ||
@@ -110,0 +111,0 @@ good1: true, |
@@ -135,2 +135,3 @@ 'use strict'; | ||
); | ||
//console.log(index); | ||
expect(index).to.deep.equal({ | ||
@@ -144,3 +145,3 @@ 'a.b.c[0]': 1, | ||
it('keysDeep', () => { | ||
let keys = _.keysDeep( | ||
let paths = _.paths( | ||
{ | ||
@@ -156,3 +157,4 @@ a: { | ||
); | ||
expect(keys).to.deep.equal([ | ||
// console.log(paths); | ||
expect(paths).to.deep.equal([ | ||
'a', | ||
@@ -273,8 +275,10 @@ 'a.b', | ||
good2: { good3: true }, | ||
bad2: { good: true }, | ||
good4: [{ good5: true }], | ||
}); | ||
clean = _.omitDeep(obj, /^bad.*$/); | ||
clean = _.omitDeep(obj, /\.?bad\d?$/); | ||
expect(clean).to.deep.equal({ | ||
good1: true, | ||
good2: { good3: true }, | ||
bad2: { good: true }, | ||
good4: [{ good5: true }], | ||
@@ -303,14 +307,524 @@ }); | ||
good1: true, | ||
good2: { good3: true, bad3: true }, | ||
good2: { good3: true }, | ||
bad2: { good: true }, | ||
good4: [{ good5: true, bad5: true }], | ||
good4: [{ good5: true }], | ||
}); | ||
clean = _.pickDeep(obj, /^good.*$/); | ||
clean = _.pickDeep(obj, /\.?good\d?$/); | ||
expect(clean).to.deep.equal({ | ||
good1: true, | ||
good2: { good3: true, bad3: true }, | ||
good2: { good3: true }, | ||
bad2: { good: true }, | ||
good4: [{ good5: true, bad5: true }], | ||
good4: [{ good5: true }], | ||
}); | ||
}); | ||
it('usage', () => { | ||
let children = [ | ||
{ | ||
description: 'description for node 1', | ||
comment: 'comment for node 1', | ||
note: 'note for node 1', | ||
name: 'node 1', | ||
bad: false, | ||
children: [ | ||
{ | ||
description: 'description for node 1.1', | ||
comment: 'comment for node 1.1', | ||
note: 'note for node 1.1', | ||
name: 'node 1.1', | ||
bad: false, | ||
}, | ||
{ | ||
description: 'description for node 1.2', | ||
comment: 'comment for node 1.2', | ||
note: 'note for node 1.2', | ||
name: 'node 1.2', | ||
good: true, | ||
}, | ||
{ | ||
description: 'description for node 1.3', | ||
comment: 'comment for node 1.3', | ||
note: 'note for node 1.3', | ||
name: 'node 1.3', | ||
bad: true, | ||
good: false, | ||
}, | ||
], | ||
}, | ||
{ | ||
description: 'description for node 2', | ||
comment: 'comment for node 2', | ||
note: 'note for node 2', | ||
name: 'node 2', | ||
good: true, | ||
children: [ | ||
{ | ||
description: 'description for node 2.1', | ||
comment: 'comment for node 2.1', | ||
note: 'note for node 2.1', | ||
name: 'node 2.1', | ||
bad: false, | ||
}, | ||
{ | ||
description: 'description for node 2.2', | ||
comment: 'comment for node 2.2', | ||
note: 'note for node 2.2', | ||
name: 'node 2.2', | ||
good: true, | ||
}, | ||
{ | ||
description: 'description for node 2.3', | ||
comment: 'comment for node 2.3', | ||
note: 'note for node 2.3', | ||
name: 'node 2.3', | ||
bad: true, | ||
good: false, | ||
}, | ||
], | ||
}, | ||
{ | ||
description: 'description for node 3', | ||
comment: 'comment for node 3', | ||
note: 'note for node 3', | ||
name: 'node 3', | ||
bad: true, | ||
good: false, | ||
children: [ | ||
{ | ||
description: 'description for node 3.1', | ||
comment: 'comment for node 3.1', | ||
note: 'note for node 3.1', | ||
name: 'node 3.1', | ||
bad: false, | ||
}, | ||
{ | ||
description: 'description for node 3.2', | ||
comment: 'comment for node 3.2', | ||
note: 'note for node 3.2', | ||
name: 'node 3.2', | ||
good: true, | ||
}, | ||
{ | ||
description: 'description for node 3.3', | ||
comment: 'comment for node 3.3', | ||
note: 'note for node 3.3', | ||
name: 'node 3.3', | ||
bad: true, | ||
good: false, | ||
}, | ||
], | ||
}, | ||
]; | ||
let out = []; | ||
function displayField(val, key, parent, context) { | ||
if (_.isArray(parent)) { | ||
key = '[' + key + ']'; | ||
} | ||
// console.log( | ||
out.push( | ||
_.repeat(' ', context.depth) + | ||
'→ ' + | ||
key + | ||
': ' + | ||
(_.isArray(val) | ||
? '[' + val.length + ']' | ||
: _.isObject(val) | ||
? '{' + (val.name || '') + '}' | ||
: val) | ||
); | ||
} | ||
// console.log('\n = Iterate over tree (each child object) = \n'); | ||
_.eachDeep(children, displayField, { tree: true }); | ||
// console.log('\n = Iterate over object (each field) = \n'); | ||
_.eachDeep(children, displayField); | ||
expect(out).to.deep.equal([ | ||
'→ [0]: {node 1}', | ||
' → [0]: {node 1.1}', | ||
' → [1]: {node 1.2}', | ||
' → [2]: {node 1.3}', | ||
'→ [1]: {node 2}', | ||
' → [0]: {node 2.1}', | ||
' → [1]: {node 2.2}', | ||
' → [2]: {node 2.3}', | ||
'→ [2]: {node 3}', | ||
' → [0]: {node 3.1}', | ||
' → [1]: {node 3.2}', | ||
' → [2]: {node 3.3}', | ||
'→ [0]: {node 1}', | ||
' → description: description for node 1', | ||
' → comment: comment for node 1', | ||
' → note: note for node 1', | ||
' → name: node 1', | ||
' → bad: false', | ||
' → children: [3]', | ||
' → [0]: {node 1.1}', | ||
' → description: description for node 1.1', | ||
' → comment: comment for node 1.1', | ||
' → note: note for node 1.1', | ||
' → name: node 1.1', | ||
' → bad: false', | ||
' → [1]: {node 1.2}', | ||
' → description: description for node 1.2', | ||
' → comment: comment for node 1.2', | ||
' → note: note for node 1.2', | ||
' → name: node 1.2', | ||
' → good: true', | ||
' → [2]: {node 1.3}', | ||
' → description: description for node 1.3', | ||
' → comment: comment for node 1.3', | ||
' → note: note for node 1.3', | ||
' → name: node 1.3', | ||
' → bad: true', | ||
' → good: false', | ||
'→ [1]: {node 2}', | ||
' → description: description for node 2', | ||
' → comment: comment for node 2', | ||
' → note: note for node 2', | ||
' → name: node 2', | ||
' → good: true', | ||
' → children: [3]', | ||
' → [0]: {node 2.1}', | ||
' → description: description for node 2.1', | ||
' → comment: comment for node 2.1', | ||
' → note: note for node 2.1', | ||
' → name: node 2.1', | ||
' → bad: false', | ||
' → [1]: {node 2.2}', | ||
' → description: description for node 2.2', | ||
' → comment: comment for node 2.2', | ||
' → note: note for node 2.2', | ||
' → name: node 2.2', | ||
' → good: true', | ||
' → [2]: {node 2.3}', | ||
' → description: description for node 2.3', | ||
' → comment: comment for node 2.3', | ||
' → note: note for node 2.3', | ||
' → name: node 2.3', | ||
' → bad: true', | ||
' → good: false', | ||
'→ [2]: {node 3}', | ||
' → description: description for node 3', | ||
' → comment: comment for node 3', | ||
' → note: note for node 3', | ||
' → name: node 3', | ||
' → bad: true', | ||
' → good: false', | ||
' → children: [3]', | ||
' → [0]: {node 3.1}', | ||
' → description: description for node 3.1', | ||
' → comment: comment for node 3.1', | ||
' → note: note for node 3.1', | ||
' → name: node 3.1', | ||
' → bad: false', | ||
' → [1]: {node 3.2}', | ||
' → description: description for node 3.2', | ||
' → comment: comment for node 3.2', | ||
' → note: note for node 3.2', | ||
' → name: node 3.2', | ||
' → good: true', | ||
' → [2]: {node 3.3}', | ||
' → description: description for node 3.3', | ||
' → comment: comment for node 3.3', | ||
' → note: note for node 3.3', | ||
' → name: node 3.3', | ||
' → bad: true', | ||
' → good: false', | ||
]); | ||
// console.log('\n = Filter tree (good children) = \n'); | ||
// console.log( | ||
// JSON.stringify(_.filterDeep(children, 'good', { tree: true }), null, 2) | ||
// ); | ||
expect(_.filterDeep(children, 'good', { tree: true })).to.deep.equal([ | ||
{ | ||
description: 'description for node 1', | ||
comment: 'comment for node 1', | ||
note: 'note for node 1', | ||
name: 'node 1', | ||
bad: false, | ||
children: [ | ||
{ | ||
description: 'description for node 1.2', | ||
comment: 'comment for node 1.2', | ||
note: 'note for node 1.2', | ||
name: 'node 1.2', | ||
good: true, | ||
}, | ||
], | ||
}, | ||
{ | ||
description: 'description for node 2', | ||
comment: 'comment for node 2', | ||
note: 'note for node 2', | ||
name: 'node 2', | ||
good: true, | ||
children: [ | ||
{ | ||
description: 'description for node 2.2', | ||
comment: 'comment for node 2.2', | ||
note: 'note for node 2.2', | ||
name: 'node 2.2', | ||
good: true, | ||
}, | ||
], | ||
}, | ||
{ | ||
description: 'description for node 3', | ||
comment: 'comment for node 3', | ||
note: 'note for node 3', | ||
name: 'node 3', | ||
bad: true, | ||
good: false, | ||
children: [ | ||
{ | ||
description: 'description for node 3.2', | ||
comment: 'comment for node 3.2', | ||
note: 'note for node 3.2', | ||
name: 'node 3.2', | ||
good: true, | ||
}, | ||
], | ||
}, | ||
]); | ||
// console.log('\n = Filter object (names of good children) = \n'); | ||
// console.log( | ||
// JSON.stringify( | ||
// _.filterDeep(children, (val, key, parent) => { | ||
// if (key == 'name' && parent.good) return true; | ||
// }), | ||
// null, | ||
// 2 | ||
// ) | ||
// ); | ||
expect( | ||
_.filterDeep(children, (val, key, parent) => { | ||
if (key == 'name' && parent.good) return true; | ||
}) | ||
).to.deep.equal([ | ||
{ | ||
children: [ | ||
{ | ||
name: 'node 1.2', | ||
}, | ||
], | ||
}, | ||
{ | ||
name: 'node 2', | ||
children: [ | ||
{ | ||
name: 'node 2.2', | ||
}, | ||
], | ||
}, | ||
{ | ||
children: [ | ||
{ | ||
name: 'node 3.2', | ||
}, | ||
], | ||
}, | ||
]); | ||
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', { tree: true }); | ||
// console.log(JSON.stringify(reallyBad, null, 2)); | ||
expect(reallyBad).to.deep.equal([ | ||
{ | ||
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, | ||
}, | ||
], | ||
}, | ||
]); | ||
// console.log('\n = Pick name and description only = \n'); | ||
// console.log( | ||
// JSON.stringify(_.pickDeep(children, ['name', 'description']), null, 2) | ||
// ); | ||
expect(_.pickDeep(children, ['name', 'description'])).to.deep.equal([ | ||
{ | ||
description: 'description for node 1', | ||
name: 'node 1', | ||
children: [ | ||
{ | ||
description: 'description for node 1.1', | ||
name: 'node 1.1', | ||
}, | ||
{ | ||
description: 'description for node 1.2', | ||
name: 'node 1.2', | ||
}, | ||
{ | ||
description: 'description for node 1.3', | ||
name: 'node 1.3', | ||
}, | ||
], | ||
}, | ||
{ | ||
description: 'description for node 2', | ||
name: 'node 2', | ||
children: [ | ||
{ | ||
description: 'description for node 2.1', | ||
name: 'node 2.1', | ||
}, | ||
{ | ||
description: 'description for node 2.2', | ||
name: 'node 2.2', | ||
}, | ||
{ | ||
description: 'description for node 2.3', | ||
name: 'node 2.3', | ||
}, | ||
], | ||
}, | ||
{ | ||
description: 'description for node 3', | ||
name: 'node 3', | ||
children: [ | ||
{ | ||
description: 'description for node 3.1', | ||
name: 'node 3.1', | ||
}, | ||
{ | ||
description: 'description for node 3.2', | ||
name: 'node 3.2', | ||
}, | ||
{ | ||
description: 'description for node 3.3', | ||
name: 'node 3.3', | ||
}, | ||
], | ||
}, | ||
]); | ||
// console.log('\n = Omit paths not ending with "e" = \n'); | ||
// console.log( | ||
// JSON.stringify( | ||
// _.omitDeep(children, /[^e]$/i, { onMatch: { skipChildren: false } }), | ||
// null, | ||
// 2 | ||
// ) | ||
// ); | ||
expect( | ||
_.omitDeep(children, /[^e]$/i, { onMatch: { skipChildren: false } }) | ||
).to.deep.equal([ | ||
{ | ||
note: 'note for node 1', | ||
name: 'node 1', | ||
children: [ | ||
{ | ||
note: 'note for node 1.1', | ||
name: 'node 1.1', | ||
}, | ||
{ | ||
note: 'note for node 1.2', | ||
name: 'node 1.2', | ||
}, | ||
{ | ||
note: 'note for node 1.3', | ||
name: 'node 1.3', | ||
}, | ||
], | ||
}, | ||
{ | ||
note: 'note for node 2', | ||
name: 'node 2', | ||
children: [ | ||
{ | ||
note: 'note for node 2.1', | ||
name: 'node 2.1', | ||
}, | ||
{ | ||
note: 'note for node 2.2', | ||
name: 'node 2.2', | ||
}, | ||
{ | ||
note: 'note for node 2.3', | ||
name: 'node 2.3', | ||
}, | ||
], | ||
}, | ||
{ | ||
note: 'note for node 3', | ||
name: 'node 3', | ||
children: [ | ||
{ | ||
note: 'note for node 3.1', | ||
name: 'node 3.1', | ||
}, | ||
{ | ||
note: 'note for node 3.2', | ||
name: 'node 3.2', | ||
}, | ||
{ | ||
note: 'note for node 3.3', | ||
name: 'node 3.3', | ||
}, | ||
], | ||
}, | ||
]); | ||
}); | ||
}); |
@@ -93,21 +93,11 @@ 'use strict'; | ||
var idList = [2, 3]; | ||
// We will need 2 passes, first - to collect needed 'id' nodes: | ||
var foundIds = _.filterDeep( | ||
var found = _.filterDeep( | ||
obj, | ||
function(value, key) { | ||
if (key == 'id' && _.indexOf(idList, value) !== -1) return true; | ||
function(value) { | ||
return _.indexOf(idList, value.id) !== -1; | ||
}, | ||
// we need to disable condensing this time to keep all the paths in result object matching source, | ||
// otherwise array indexes may be changed and we will not find correct values in the source object later. | ||
{ condense: false } | ||
{ tree: true } | ||
); | ||
// second pass - to put missed 'title' nodes both for found ids and their parents. | ||
var filtrate = _.filterDeep(obj, function(value, key, parent, ctx) { | ||
if ( | ||
_.get(foundIds, ctx.path) !== undefined || | ||
(key == 'title' && _.get(foundIds, ctx.parent.path) !== undefined) | ||
) | ||
return true; | ||
}); | ||
expect(filtrate).to.deep.equal([ | ||
expect(found).to.deep.equal([ | ||
{ | ||
@@ -350,25 +340,11 @@ title: 'category 1', | ||
var endsWith = 'foo'; | ||
// We will need 2 passes, first - to collect needed 'name' nodes ending with 'foo': | ||
var foundFoo = _.filterDeep( | ||
obj, | ||
function(value, key) { | ||
if (key == 'name' && _.endsWith(value, endsWith)) return true; | ||
return _.endsWith(value.name, endsWith); | ||
}, | ||
// we need to disable condensing this time to keep all the paths in result object matching source, | ||
// otherwise array indexes may be changed and we will not find correct values in the source object later. | ||
{ condense: false } | ||
{ tree: { children: ['sub_categories', 'indicators'] } } | ||
); | ||
// console.log(foundFoo); | ||
// second pass - to add missed fields both for found 'foo' nodes and their parents. | ||
var filtrate = _.filterDeep(obj, function(value, key, parent, ctx) { | ||
if ( | ||
_.get(foundFoo, ctx.path) !== undefined || | ||
(!_.isObject(value) && _.get(foundFoo, ctx.parent.path) !== undefined) | ||
) { | ||
// if (_.has(foundFoo, path)) console.log(`${key} has ${path}`); | ||
// else console.log(`${key} has parent ${parentPath}`); | ||
return true; | ||
} | ||
}); | ||
expect(filtrate).to.deep.equal([ | ||
expect(foundFoo).to.deep.equal([ | ||
{ | ||
@@ -450,23 +426,14 @@ id: 1, | ||
var keyword = 'Hit'; | ||
// We will need 2 passes, first - to collect needed nodes with 'Hit' value: | ||
var foundHit = _.filterDeep( | ||
input, | ||
function(value) { | ||
if (value.value && value.value.includes(keyword)) return true; | ||
return value.value.includes(keyword); | ||
}, | ||
{ | ||
condense: false, // keep empty slots in array to preserve correct paths | ||
leavesOnly: false, | ||
tree: true, | ||
onTrue: { skipChildren: true }, | ||
} | ||
); | ||
// second pass - to add missed fields both for found 'Hit' nodes and their parents. | ||
var filtrate = _.filterDeep(input, function(value, key, parent, ctx) { | ||
if ( | ||
_.get(foundHit, ctx.path) !== undefined || | ||
_.get(foundHit, ctx.parent.path) !== undefined | ||
) { | ||
return true; | ||
} | ||
}); | ||
expect(filtrate).to.deep.equal([ | ||
expect(foundHit).to.deep.equal([ | ||
{ | ||
@@ -473,0 +440,0 @@ value: 'Miss1', |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
223673
39
4558
670