Reputation: 8580
I've seen lots of solutions for "deep mapping" over an object to change either the keys of the object, or the value of the object, but not both.
For example if given the following object:
const obj = {
this: {
is_not: {
something: "we want to keep"
},
is: {
a: {
good_idea: 22
},
b: {
bad_idea: 67
},
c: [{
meh_idea: 22
}]
}
}
}
Essentially I'd like to be able to transform it with the following function signature:
const newObj = deepTransform(obj, iterator)
function iterator (key, value) {
if (key.endsWith("idea")) return { [key.replace("idea", "")]: value + 1}
else if (key === "is_not") return {}
else return {[key]: value}
}
Running deepTransform would result in an object that looks like:
{
this: {
is: {
a: {
good: 23
},
b: {
bad: 68
},
c: [{
meh: 23
}]
}
}
}
If anyone could help me create this function that would be awesome! I'd be more than happy if it used lodash functions under the hood.
Please ask if anything is unclear. Thanks!
Upvotes: 3
Views: 992
Reputation: 191976
You can use lodash's _.transform()
to recursively iterate objects/arrays, and rebuild them with updated keys and values:
const { transform, toPairs, isObject } = _
const deepTransform = (obj, iterator) => transform(obj, (acc, val, key) => {
const pair = toPairs(iterator(key, val))[0] // use the iterator and get a pair of key and value
if(!pair) return // if pair is undefined, continue to next iteration
const [k, v] = pair // get the update key and value from the pair
// set the updated key and value, and if the value is an object iterate it as well
acc[k] = isObject(v) ? deepTransform(v, iterator) : v
})
const obj = {"this":{"is_not":{"something":"we want to keep"},"is":{"a":{"good_idea":22},"b":{"bad_idea":67},"c":[{"meh_idea":22}]}}}
const iterator = (key, value) => {
if (String(key).endsWith("_idea")) return { [key.replace("_idea", "")]: value + 1}
if (key === "is_not") return {}
return {[key]: value}
}
const newObj = deepTransform(obj, iterator)
console.log(newObj)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js" integrity="sha512-90vH1Z83AJY9DmlWa8WkjkV79yfS2n2Oxhsi2dZbIv0nC4E6m5AbH8Nh156kkM7JePmqD6tcZsfad1ueoaovww==" crossorigin="anonymous"></script>
Changing the iterator a to return a pair of [key, value], would make _.toPairs()
redundant, and would simplify the code:
const { transform, isObject, isUndefined } = _
const deepTransform = (obj, iterator) => transform(obj, (acc, val, key) => {
const [k, v] = iterator(key, val) // use the iterator and get a pair of key and value
if(isUndefined(k)) return // skip if no updated key
// set the updated key and value, and if the value is an object iterate it as well
acc[k] = isObject(v) ? deepTransform(v, iterator) : v
})
const obj = {"this":{"is_not":{"something":"we want to keep"},"is":{"a":{"good_idea":22},"b":{"bad_idea":67},"c":[{"meh_idea":22}]}}}
const iterator = (key, value) => {
if (String(key).endsWith("_idea")) return [key.replace("_idea", ""), value + 1]
if (key === "is_not") return []
return [key, value]
}
const newObj = deepTransform(obj, iterator)
console.log(newObj)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js" integrity="sha512-90vH1Z83AJY9DmlWa8WkjkV79yfS2n2Oxhsi2dZbIv0nC4E6m5AbH8Nh156kkM7JePmqD6tcZsfad1ueoaovww==" crossorigin="anonymous"></script>
Upvotes: 3