1

I have a complex question that I can't figure out the answer to. So in my Discord bot, I have a little object called config. I also have a baseConfig object, which are the defaults used when the bot is added to a new server. Sometimes, I update my bot in a way that requires a new config value- I want to be able to add it to baseConfig and have the new key reflected across config. (In reality the config object has nested objects for different guilds, but I'm writing it in a way where this is irrelevant). As an example, let's say obj1 is a config, and obj2 is the base config.

let obj1 = {
   emojiSettings: {
      success: ":success:",
      error: ":error:"
   },
   warnSettings: {
      muteThreshold: 3,
      expireHours: 48
   }
}

Let's say I update obj2 with a brand new config:

// defaults
let obj2 = {
   emojiSettings: {
      success: ":defaultSuccess:"
      error: ":defaultError:"
   }
   warnSettings: {
      muteThreshold: 2,
      expireHours: 24
   }
   newConfig: {
      newOption: 4
   }
}

I want all the settings in obj1 to stay the same, but add the objects and keys that were updated in obj2.

  • `{...obj1,...obj2}` – cmgchess Jul 14 '22 at 18:42
  • That doesn't work. It resets the values in obj1 to default. I only want to add nonexistent values, not change existing ones –  Jul 14 '22 at 18:54
  • didnt read properly. `{...obj2,...obj1}` should ovewrite obj2 changes with obj1 – cmgchess Jul 14 '22 at 18:59
  • Still doesn't work, unless you intend to turn object 2 into object 1? it doesn't do anything except reset all values –  Jul 14 '22 at 21:39
  • Check out this one which works just fine if you do not want to use some third party library like lodash: https://stackoverflow.com/a/383245/1047662 – Cyclonecode Jul 14 '22 at 21:47

1 Answers1

0

Update

A much, much simpler solution than the one I suggested below would be to just use object destructuring. Probably would be smart to have a base config object and a custom config object that join into a computed config object that's the one you actually use (so your custom config settings can be saved and updated later), though you could also reassign config if you prefer.

Pass the base config first to apply all of its properties to the config object, then pass the config to update the fields that are different.

const computedConfig = {...baseConfig, ...customConfig}
// Or, if you don't want a third object:
obj1 = {...obj2, ...obj1}

Old answer

I would write a function to deeply compare the two that iterates over every property. If the property doesn't exist in obj1, it adds it. If the property does exist in obj1, it checks to see if that property is an object (but not an array), and, if it is, the function recursively calls itself to make sure all of that object's properties also exist in obj1.

const updateObject = (o1, o2) => {
  Object.entries(o2).forEach((prop) => {
    const propName = prop[0];
    if (!o1[propName]) {
      if (typeof o2[propName] !== 'object' || Array.isArray(o2[propName])) {
        o1[propName] = o2[propName];
      } else {
        o1[propName] = { ...o2[propName] };
      }
    } else if (typeof o2[propName] === 'object' && !Array.isArray(o2[propName])) {
      updateObject(o1[propName], o2[propName]);
    }
  });
};

updateObject(obj1, obj2);
Cutler.Sheridan
  • 170
  • 2
  • 9