Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deep map/transform js object with ability to alter keys and values

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!

like image 978
tnrich Avatar asked Nov 24 '25 10:11

tnrich


1 Answers

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>
like image 64
Ori Drori Avatar answered Nov 26 '25 22:11

Ori Drori



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!