0

Say I have a "base" object like this:

{
  foo: {
    bar: {
      baz: 123
    },
    hello: 'world',
    something: {
      deeply: {
        nested: true
      }
    }
  }
}

Then say I want to "extend" that nested object with another object, thereby "adding" properties to the base object, but without mutably changing it, and without also just recreating every object in the tree! It should be somewhat optimized by only recreating objects which need to be recreated, so only doing { ...x } where it needs to be done because we are changing something...

So say I extend our base object with an "updates" object that looks like this:

{
  foo: {
    hello: 'earth',
    something: {
      deeply: {
        iAmNew: 32134
      }
    }
  }
}

The end result is obviously this:

{
  foo: {
    bar: {
      baz: 123
    },
    hello: 'earth',
    something: {
      deeply: {
        nested: true,
        iAmNew: 32134
      }
    }
  }
}

But it should not recreate the { baz: 123 } object, because nothing changed there.

In my situation, I have a complex, deeply nested (up to eight levels deep) settings object. So if every time you changed a setting it recreated this whole object, that seems inefficient. It should just change the parts that change, while leaving the rest intact.

Can it be done easily?

export function extend(
  start,
  updates,
) {
  const end = { ...start }

  let node = end

  for (const i in updates) {
    const val = updates[i]
    if (val != null) {
      if (typeof val === 'object') {
      }
    }
  }

  return end
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Lance
  • 75,200
  • 93
  • 289
  • 503
  • Presumably this object contains only primitives for values? What about arrays? – kelsny Apr 14 '23 at 14:03
  • Also this looks like a Typescript question. It's not clear what the intended parameters are for that function. Would you pass in something like a path to the sub-object to be updated? – Pointy Apr 14 '23 at 14:05
  • No the values can be arrays, strings, booleans, numbers, nulls, basically any JSON value. – Lance Apr 14 '23 at 14:05
  • 1
    @jonrsharpe I assumed deepMerge (which you can do with lodash I think), would clone every property in the trees, which I would like to avoid if possible. That's the whole point of this question. Is that correct assumption? – Lance Apr 14 '23 at 14:06
  • If you extend object A to create B, and then modify A, should those modifications be visible in B? – Andrew Parks Apr 14 '23 at 14:07
  • @AndrewParks you will never mutably change these objects, so you will never directly modify A. – Lance Apr 14 '23 at 14:08
  • 3
    Did you try _validating_ that assumption? The proposed dupe has quite a few different answers. – jonrsharpe Apr 14 '23 at 14:11
  • At first glance it seems like you're looking for persistent data-structures. For example there's [immutable-js](https://www.npmjs.com/package/immutable) package which provide some of them – Jaood_xD Apr 14 '23 at 14:16
  • Home grown, type-safe implementation that does what you asked: https://tsplay.dev/mxkE1N – kelsny Apr 14 '23 at 14:33
  • if `{ baz: 123 }` should disappear, what about `bar` or `nested: true` elements, should they have to disappear too ? – Mister Jojo Apr 14 '23 at 14:35

1 Answers1

-1

This approach will create a shallow clone of the original, and add in extra properties.

const a = {
  foo: {
    bar: {
      baz: 123
    },
    hello: 'world',
    something: {
      deeply: {
        nested: true
      }
    }
  }
}

const updates = {
  foo: {
    hello: 'earth',
    something: {
      deeply: {
        iAmNew: 32134
      }
    }
  }
}

function f(a, b) {
  let c = {...a};
  Object.entries(b).forEach(([k,v])=>
    c[k] = !Object.hasOwn(c, k) || typeof v !== 'object' ? v : f(c[k],v)
  )
  return c;
}

console.log(f(a, updates));
console.log(a);
Andrew Parks
  • 6,358
  • 2
  • 12
  • 27