How to deep merge without overwriting exisiting vales
Within a game im currently developing, I keep all the game/save data in a big JS object.
for example:
const save = {
inventory: {
equips: {
weapon: {...},
chestplate: {...}
},
money: 50123
},
gameBooleans: {
isThisUnlocked: false,
isThatUnlocked: false
},
settings: {
version: '1.3.4'
}
}
Whenever I push out a new update, I check to see if the save.settings.version
equals to the newest version. If it doesn't, I update the old save data to include the new values.
My problem right now is that as I develop the game, I add new fields to the save data. Eg. save.gameBooleans.isThisNewThingUnlocked: false
.
Previously, what I have been doing is manually adding each new field to the object when the game detects an older save:
const currentVersion = '1.3.3'
if (save.settings.version === '1.3.2') {
save.gameBooleans.isThisNewThingUnlocked = false
save.gameBooleans.isThisOtherNewThingUnlocked = false
...etc
save.settings.version = currentVersion
}
This is becoming quite tedious and I sometimes miss a field which breaks the game and gets my thousands of players upset haha. I was wondering if there was a better approach.
I tried using lodash deepmerge but if I use it like this: _.mergeDeep(upToDateState, oldSave)
, it doesnt add in the new key/fields. And when used the other way: _.mergeDeep(oldSave, upToDateState)
, this adds in the new values but overwrites some existing values.
I dont want to write my own function to handle this but its starting to look like I have to. Does anyone have any ideas?
Solution 1:
You can use the Object.keys()
function to iterate through eack key of the new object and make a full copy in the save
object including the new keys, withoud doing it manually. The function should be like this
function deepCopy(old_, new_) {
// Iterate through each key of the new object
Object.keys(new_).forEach(key => {
//If there is a nested object, recall the function
if (typeof new_[key] === 'object' && ! Array.isArray(new_[key]) && new_[key] !== null)
deepCopy(old_[key], new_[key])
// If there is a non-object value, just copy the value
else
old_[key] = new_[key];
});
}
Take into account that this function would make a reference copy of array values (which may be undesirable). However, you can make a new conditional to handle arrays and make a value copy using the spread operator newArray = ...oldArray
.
Try it here
I created a new object called update
which has 2 new keys in the gameBooleans
value:
const save = {
gameBooleans: {
isThisUnlocked: false,
isThatUnlocked: false
},
settings: {
version: '1.3.4'
}
}
const update = {
gameBooleans: {
isThisUnlocked: true,
isThatUnlocked: true,
foo: true,
bar: true,
},
settings: {
version: '1.3.4'
}
}
function deepCopy(old_, new_) {
Object.keys(new_).forEach(key => {
if (typeof new_[key] === 'object' && ! Array.isArray(new_[key]) && new_[key] !== null)
deepCopy(old_[key], new_[key])
else
old_[key] = new_[key];
});
}
deepCopy(save, update)
console.log(save)