Saving Nested Changes from Data Structure Dictionary To Player's Data Dictionary

Say for example I have a data structure like this:

local DataStructure = {
  XP = {
    BlackTeam = {
      XP = 10
    },
  RedTeam = {
      XP = 10
    },
  BlueTeam = {
      XP = 10
    }
}

And I add SkillPoints into only RedTeam, how can I ensure my PlayerData (that I receive from GetAsync of the datastore) will have this change?

I’ve tried using recursion but I don’t believe I’m using it correctly.

And that this change will occur no matter how deep the change is in terms of nesting (ex. inside 7 tables or such).

When you get the data using GetAsync, you can fill in all missing keys by looping through your DataStructure and checking what’s missing.

local receivedData = --get your data using GetAsync
for teamName, teamData in pairs(DataStructure) do
    for key, value in pairs(teamData) do
        if not receivedData[teamName] or not receivedData[teamName][key] then
             --Key is missing! Fill in the default from the DataStructure
             receivedData[teamName][key] = DataStructure[teamName][key]
        end
    end
end

Usually it would just be one for loop, but since you have a 2D dictionary it will have to be a loop inside of a loop

To ensure that PlayerData changes accordingly, you could simply update PlayerData whenever updating SkillPoints. If the PlayerData is in a script that is separated from your data structure, you could send over the information through a ModuleScript, or by using BindableEvents/BindableFunctions.

How would this work if my actual data structure has many nested tables within each other? For example, I have a nested table about 5 deep and such, but I still want to be able to add indexes I add in there to be added to Player Data

Oh okay, if there won’t be a set amount of levels, you should check if each variable is a table using typeof(). If it is, loop through it, checking for tables inside the table, etc. until you get to the bottom of it.

I came up with something like this. As you can see, the “FillMissingKeys” function is ran as many times as needed because it uses itself if it finds a table.
Not entirely sure if this will work correctly but try it out. :slight_smile:

local DataStructure = {}

local function FillMissingKeys((Table, path)
	--As we go multiple levels into the table, one key won't suffice so we have a path (which is a table) containing all the keys
	local relevantPartOfDataStructure = DataStructure
	for _, key in pairs(path) do
		relevantPartOfDataStructure = relevantPartOfDataStructure[key]
	end
	
	for key, value in pairs(relevantPartOfDataStructure) do
		if typeof(value) == "table" then
			--A table was found, add to the path and descend a level deeper
			table.insert(path, key)
			Table[key] = FillMissingKeys((value, path)
		else
			Table[key] = relevantPartOfDataStructure[key]
		end
	end
	return Table
end

local receivedData = {}--get your data using GetAsync

--We want to start from the top, so the path will be an empty table
receivedData = FillMissingKeys(receivedData, {})

Works!

Would this also work to set it to nil and change a couple of things in the function to detect values that do not existing the DataStucture anymore?

Hmm, you would have to make another function that is a reversed version of this.
Instead of looping through the DataStructure and checking if the keys exist in your data, you would have to loop through the data and check if it exists in the DataStructure.
Try the following function.

local DataStructure = {}

local function RemoveExtraKeys(Table, path)
	--As we go multiple levels into the table, one key won't suffice so we have a path (which is a table) containing all the keys
	local relevantPartOfDataStructure = DataStructure
	for _, key in pairs(path) do
		relevantPartOfDataStructure= relevantPartOfDataStructure[key]
	end
	
	for key, value in pairs(Table) do
		if typeof(value) == "table" then
			--A table was found, add to the path and descend a level deeper
			table.insert(path, key)
			Table[key] = RemoveExtraKeys(value, path)
		else
			if not relevantPartOfDataStructure[key] then
			    --Key wasn't found in DataStructure, remove it
			    table.remove(Table, key)
			end
		end
	end
	return Table
end

local receivedData = {}--get your data using GetAsync

--We want to start from the top, so the path will be an empty table
receivedData = RemoveExtraKeys(receivedData, {})

There is probably a way to merge it into a one function with the FillMissingKeys() one, but you could just run both of the functions like this

receivedData = RemoveExtraKeys(receivedData, {})
receivedData = FillMissingKeys(receivedData, {})

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.