Comparing two tables to update one

I’m trying to make my data system automatically update old player’s data to the new template. This could mean that certain values might be renamed, removed, or added. Currently, I just have a long list that says: “If this value isn’t in the table then just add it.” This still leaves renaming things like tables with values in them. I’m not really sure how to go about doing this.

So far, I’ve been able to loop through the template data and look for subtables, and if found it will fire the same function again for that table. When not finding a table (meaning you found a value), I’m not sure how to handle telling if I need to add a value/table (with what name?), rename a value/table (to what?), or remove a value/table. Is there a different better approach?

Here’s some example data of the problem to help the discussion:

local newTemplateData = {
	Nope = { --Entire table renamed
		ThisValue = false
		--Value removed
	},
	AlsoThisValue = 123,
	ExtraValue = "Uh oh",
	AnotherValue = "Yay!" --Added
}

local oldPlayerData = {
	Woah = {
		ThisValue = false,
		ThatValue = "Hi!"
	},
	AlsoThisValue = 123,
	ExtraValue = "Uh oh"
}
1 Like

You can use or to replace a value that is equal to nil. When updating my game, I use

data.Value = loadedDatastore[5] or {false, false, false}

If loadedDatastore[5] has nothing in it, data.Value will be set to {false, false, false}

Hope this helps.

Is there any way to implement this so you wouldn’t have to have a default value set (in this case its the {false, false, false})? Ideally, it would you could have a function that takes a took at those two tables mentioned and fixes any differences between the old data and the new template.

Try using

if loadedDatastore[5] == nil then
 print("There is nothing in loadedDatastore[5]!")
end

Apologies, I should’ve been more clear. This is what my current system is doing but I’m looking to make a function that automatically does this for me by comparing the two tables, seeing what changes need to be done, then applying the changes.

1 Like

Have you tried using table.insert?
Still not entirely understanding what you are going for. Are you trying to update based on another table? For example…

local OldData = {1,1,1}
local NewData = {2,2,2}
if Data ~= NewData then Data = NewData end

Let’s take this example player data for an example:

local newTemplateData = {
	Nope = { --Entire table renamed
		ThisValue = false
		--Value removed
	},
	AlsoThisValue = 123,
	ExtraValue = "Uh oh",
	AnotherValue = "Yay!" --Added
}

local oldPlayerData = {
	Woah = {
		ThisValue = false,
		ThatValue = "Hi!"
	},
	AlsoThisValue = 123,
	ExtraValue = "Uh oh"
}

If the old player data is what somebody has saved now, that wouldn’t be compatable with the latest version of the game which would use the newest template. I want to automatically update old player data tables with the new ones without resetting values. For example, the function would see that the newest iteration of the template doesn’t have the “ThisValue” variable inside, so it would remove it. Another example: the function would notice that the “Woah” table was renamed to “Nope” so it would rename it to match the template. This would go on until the template matches the player’s data but not reset any values, just rename, remove, or add values based on what is required in the template.

1 Like

I’m not entirely sure why you would need to iterate over the table at all, unless I’m missing something to do with the specifics of your data structures. Why not just copy the new template and change the values of the copy to match the player’s data from their old saved data structure?

local newTemplate = {
	Nope = { --Entire table renamed
		ThisValue = false
		--Value removed
	},
	AlsoThisValue = 123,
	ExtraValue = "Uh oh",
	AnotherValue = "Yay!" --Added
}

local function deepCopy(original)
    local copy = {}
    for k, v in pairs(original) do
        if type(v) == "table" then
            v = deepCopy(v)
        end
        copy[k] = v
    end
    return copy
end

local function convertData(oldData)
    local newData = deepCopy(newTemplate)
    --changing AlsoThisValue to old data's AlsoThisValue, for example
    newData.AlsoThisValue = oldData.AlsoThisValue

    return newData
end

You can use this function to iterate through the table and it’ll add any missing indexes to the data that was loaded.

OriginalData is the updated data, LoadedData is the data that’s loaded from the player (no matter if they’re updated or not)

local function halfMatch(OriginalData, LoadedData)
	LoadedData = LoadedData or {}
	for i,v in pairs (OriginalData) do
		if (type(v) == "table") then
			LoadedData[i] = halfMatch(v, LoadedData[i])
		elseif (LoadedData[i] == nil) then
			LoadedData[i] = v
		end
	end
	return LoadedData
end

This setup would mostly work, but if a value gets moved to a different position in the table it wouldn’t work. Also, renaming variables would just create a new variable.


In regards to @vastqud’s response:
I do this exact method but much more simple. I take the current player’s data then just apply specific changes that are required to make the old data compatible with the new data. I was hoping to automate this process so I don’t have to manually make changes like that.

Try experimenting with metatables especially __index and __newindex:

Hoping this can be bumped because I am having the same question right now.

1 Like

Ok after a bit of debugging turns out this was a lot easier than I thought. For any future readers here is the solution.

local function CleanTable(oldData, template)
    local newData = {}

    for key, value in pairs(oldData) do
        if type(value) == "table" and template[key] ~= nil then
            newData[key] = CleanTable(value, template[key])
        else
            if template[key] ~= nil then
                newData[key] = value 
            end
        end
    end

    return newData
end

As for adding data here is a good way → API - ProfileService

2 Likes