Use Case
_.pick
creates a shallow clone of an object given a predicate that identifies which keys to keep. pickDeep
would perform a deep clone of the object and would "pick" up all nested objects containing the given keys. All containers containing the nested objects would remain and would not be removed.
Requirements
[Update as per comments]
- Recursively apply a
pick
to each level in an object. - If a property is an
object
/array
, then apply apick
. - Keep all
object
/array
only if the descendent properties fulfil thepick
.
Test Cases
var testCase1 = { a: 5, b: 6};
pickDeep(testCase1, 'a'); // {a : 5}
var testCase2 = { a: 5, b: { a: 1}, c: { b: 3}};
pickDeep(testCase2, 'a'); // {a: 5, b: {a: 1}}
var testCase3 = { a: 5, b: [ { a: 1}, { c: 2}, {d: 3} ], c: [ {c: 2}, {d : 3}]}
pickDeep(testCase3, 'a'); //{ a: 5, b: [ {a: 1} ]}
var testCase4 = [ {a: 5}, {b: 2}, {c: {a :3 }}, {d: [ {a: 4}] }, z: [ { f: 34}] ];
pickDeep(testCase4, 'a'); // [ {a:5}, {c: {a:3}}, {d: [ {a:4}]}];
Improvements
I'd like any constructive criticism to improve this code, but advice I'm mainly interested in are:
- Readability
- Flexibility
- Performance
Update: changed method signature to leverage _.pick
.
I have a suspicion that the method signature could be improved to be more inline with functional standards in lodash/underscore.
Another idea I had, but haven't had a chance to try is performing a _.pick
using the predicate that checks if the key is in keys
or if the key _.isObject
, then recursing on every object
property.
Question
Is there a way to implement this functionality using _.cloneDeep
and _.pick
together?
Code jsfiddle
Function
function pickDeep(collection, identity, thisArg) {
var picked = _.pick(collection, identity, thisArg);
var collections = _.pick(collection, _.isObject, thisArg);
_.each(collections, function(item, key, collection) {
var object;
if (_.isArray(item)) {
object = _.reduce(item, function(result, value) {
var picked = pickDeep(value, identity, thisArg);
if (!_.isEmpty(picked)) {
result.push(picked);
}
return result;
}, []);
} else {
object = pickDeep(item, identity, thisArg);
}
if (!_.isEmpty(object)) {
picked[key] = object;
}
});
return picked;
}
Test Data
var data = {
a: 5,
b: 6,
c: 7,
d: {
a: 65,
z: 6,
d: {
a: 65,
k: 5
}
},
e: [
{a : 5},
{b : 6},
{c : 7}
],
f: [
{
b : [ { a: 5, z: 5 } ],
c : 6
},
{
g: 0
}
]
};
Execution Code
function isIn(collection) {
return function(value, key) {
return _.contains(collection, key);
}
}
console.log(pickDeep(data, isIn(['a', 'c'])));
_.pick
in logic that allows for a deep traverse, rather than re-implementing the pick functionality. – Pete Jul 25 '14 at 18:59_.cloneDeep
and_.pick
together, but I haven't been able to connect the dots. – Pete Jul 28 '14 at 14:01pick
always returns an object. But your test cases want it to return an array? Your code fails testCase4 due to that consideration – megawac Jul 28 '14 at 14:35