Changing one nested dictionary value changes the same nested values for all members of the dictionary

Very bizzare bug

Replicated = game.ReplicatedStorage
Dictionary = {}
Fighters = {"Ryu", "Ken", "Chun Lee"}

-- Set default parameters for characters
DefaultStatistics = {
["MaxHP"] = 100,
["Walkspeed"] = 16,
["JumpPower"] = 50,
}

--Create the dictionary
for index, value in pairs(Fighters) do
	Dictionary[Fighters[index]] =  { 
		["Name"] = Fighters[index],
		["Statistics"] = DefaultStatistics,
		["Skills"] = {},
		["Model"] = DefaultCharacter
	}
end

Ryu = Dictionary["Ryu"]
Ryu["Statistics"]["Walkspeed"] = 50
Ryu["Statistics"]["MaxHP"] = 100
Ryu["Skills"]["Jab"] = {
	["Displayname"] = "Dragon Rush",
	["SkillName"] = "Jab",
	["Input"] = "PrimaryFire",	
	["Damage"] = {2, 5, 15},
	["Speed"] = 20,
	["Dash Length"] = 2,
	["Knockback"] = 100
}

print(Dictionary)
local GetData = {Dictionary}
return GetData

print(Dictionary) Returns…

                    ["Ryu"] =  â–Ľ  {
                       ["Name"] = "Ryu",
                       ["Skills"] =  â–Ľ  {
                          ["Jab"] =  â–¶ {...}
                       },
                       ["Statistics"] =  â–Ľ  {
                          ["JumpPower"] = 50,
                          ["MaxHP"] = 150,
                          ["Walkspeed"] = 50

                    ["Ken"] =  â–Ľ  {
                       ["Name"] = "Ken",
                       ["Skills"] = {},
                       ["Statistics"] =  â–Ľ  {
                          ["JumpPower"] = 50,
                          ["MaxHP"] = 150,
                          ["Walkspeed"] = 50
                       }

                    ["Chun Lee"] =  â–Ľ  {
                       ["Name"] = "Chun Lee",
                       ["Skills"] = {},
                       ["Statistics"] =  â–Ľ  {
                          ["JumpPower"] = 50,
                          ["MaxHP"] = 150,
                          ["Walkspeed"] = 50
                       }

When this code executes, it correctly changes [“Jab”] for only the instance [“Ryu”][“Skills”]. That means it doesn’t set [“Chun Lee”][“Skills”] or [“Ken”][“Skills”] to have the value [“Jab”], only [“Ryu”].
However, it changes all instances of descendants of [“Dictionary”] to have [“Statistics”][“Walkspeed”] = 50 and [“MaxHP”] = 150, when it sohuld only effect [“Ryu”][“Statistics”]. I’m confused as to why this behavior happens.

1 Like

DefaultStatistics is one table, when you’re setting this table in another table you’re not actually cloning the table but rather referencing the old table.

So when you make a change to one item’s Statistics key it actually modifies that single table. Hence, Ryu, Ken, and Chun Lee all see Walkspeed = 50, because they’re looking at the same table.

I’d recommend using table.clone so there can be multiple tables that hold the same values, but not point to the exact same table.

for index, value in pairs(Fighters) do
	Dictionary[Fighters[index]] =  { 
		["Name"] = Fighters[index],
		["Statistics"] = table.clone(DefaultStatistics),
		["Skills"] = {},
		["Model"] = DefaultCharacter
	}
end
3 Likes

This worked! Thank you
keystrokes

1 Like

@SyntaxMenace is correct. There is one more potential pitfall though if you use table.clone() to be aware of, which is that clone only does a shallow copy. So if your “Statistics” dictionary at some point gets a key for which the value is another table, the problem will come back because the nested tables will again be references to the same data.

For example, if you had a table like this…

["Statistics"] = {
    ["MaxHP"] = 150,
    ["SpeedRange"] = {
        ["Min"] = 8,
        ["Max"] = 20,
    },
}

… you’ll have all players sharing the same min and max speed values, because all of their Statistics tables will be pointing to the same SpeedRange table. If you need a recursive deep copy, you have to write your own, carefully. :slight_smile: Or just make sure the tables you clone are always flat, with no tables as values.

1 Like

Thanks for your insight!
Can I ask, what if I used the same method to copy the other table? So in this case, copy Statistics, but then also copy SpeedRange into Statistics?

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