Issue With Updating Data When Multiple Nested Tables With Keys Exist in Same Table

The app I am currently developing is very data intensive, so I am trying to use nested tables and nested tables with key values to organize data. The issue I am having arises after I insert tables of data with key values into an existing table, and then try to update data assigned to key values in these inserted tables. I have duplicate key data that exists across multiple nested tables, so when I reassign one of the key values a new value, it updates all of the key values that exist across all of the nested tables, not just the one I am trying to update. Below is a code sample that illustrates the problem:

local table = { { "vacant", "vacant" }, { "vacant", "vacant" } } local nestedTable = { hello = "good-bye", something = "nothing", back = "forwards" } table[1][1] = nestedTable table[1][2] = nestedTable table[2][1] = nestedTable table[2][2] = nestedTable table[1][1].hello = "forever" table[1][2].hello = "eternity" print( table[1][1].hello ) print( table[1][2].hello ) print( table[2][1].hello ) print( table[2][2].hello )

The output from the code above should look like this:

forever

eternity

good-bye

good-bye

But, instead the output looks like this:

eternity

eternity

eternity

eternity

Any feedback as to what might be causing this problem or alternative ways to update key information without updating all the other keys in other nested tables would be appreciated! Thanks

Hi @crawfdc47,

This is a pretty common thing that people bump into with Lua. Unlike other object types (strings, numbers), which create a distinct duplicate value when you set a new variable value by referencing an existing one, when you set a new variable’s value by referencing an existing table’s variable, you are making a reference to that specific table, and not creating a duplicate of it.

So by calling these 4 lines of code:

table[1][1] = nestedTable table[1][2] = nestedTable table[2][1] = nestedTable table[2][2] = nestedTable

You are making all 4 variables point to the same table. Hence, by making a change to one of them, you are making a change to all of them - because in actuality there’s only one “nestedTable”, you’ve just created 4 variables that can be used interchangeably to arrive at the same destination.

There are a number of ways you can work around this. The first one (and this is the least elegant solution) is to specify each table as you create the variable:

table[1][1] = { hello = "good-bye", something = "nothing", back = "forwards" } table[1][2] = { hello = "good-bye", something = "nothing", back = "forwards" } table[2][1] = { hello = "good-bye", something = "nothing", back = "forwards" } table[2][2] = { hello = "good-bye", something = "nothing", back = "forwards" }

Another solution would be to create a function that returns a new instance of your default “nestedTable”, and call that function when repopulating your table:

local function newNested() return { hello = "good-bye", something = "nothing", back = "forwards" } end table[1][1] = newNested() table[1][2] = newNested() table[2][1] = newNested() table[2][2] = newNested()

But here’s another way to accomplish it, and it’s handier because it is re-usable in other parts of your app. Here’s a handy little “makeCopy” function that you can use any time you want to make a copy of an existing variable, regardless of the variable’s type - it’ll always return a distinct duplicate of the original, including subtables:

function makeCopy(original) if original == nil then print("makeCopy Error: No original specified!") return nil end local copy if type(original) == "table" then copy = {} for k,v in pairs(original) do copy[k] = makeCopy(v) end else copy = original end return copy end

So if you include the makeCopy function in your project, then you could modify your code like this to get the desired result:

local nestedTable = { hello = "good-bye", something = "nothing", back = "forwards" } table[1][1] = makeCopy(nestedTable) table[1][2] = makeCopy(nestedTable) table[2][1] = makeCopy(nestedTable) table[2][2] = makeCopy(nestedTable)

Hope this helps! :slight_smile:

Great answer @schroederapps!

But let me add on to this. You might want to avoid naming your table “table” since that’s the name of the table object. You will make things like table.insert(), table.sort() inaccessible as long as your data table named “table” in scope.

Rob

Hi @schroederapps,

Thank you very much for the reply - I agree with Rob that it was a great answer! I implemented the makeCopy() function you provided, and it solved the issue for me. From the way you explained it, I think I understand the logic behind why I was getting the duplicated results before also. It’s definitely an easy issue to get hung up on if you aren’t super familiar with how data assignment works, so I’m glad you clarified it for me.

Also, thanks Rob for the tip about avoiding using “table” as the name of my table variable. I was actually getting errors when experimenting with table.insert and table.remove functions to try and solve my problem, so was planning on posting this as a follow-up question at some point. But, your explanation answered this question for me. Much appreciated!

Hi @crawfdc47,

This is a pretty common thing that people bump into with Lua. Unlike other object types (strings, numbers), which create a distinct duplicate value when you set a new variable value by referencing an existing one, when you set a new variable’s value by referencing an existing table’s variable, you are making a reference to that specific table, and not creating a duplicate of it.

So by calling these 4 lines of code:

table[1][1] = nestedTable table[1][2] = nestedTable table[2][1] = nestedTable table[2][2] = nestedTable

You are making all 4 variables point to the same table. Hence, by making a change to one of them, you are making a change to all of them - because in actuality there’s only one “nestedTable”, you’ve just created 4 variables that can be used interchangeably to arrive at the same destination.

There are a number of ways you can work around this. The first one (and this is the least elegant solution) is to specify each table as you create the variable:

table[1][1] = { hello = "good-bye", something = "nothing", back = "forwards" } table[1][2] = { hello = "good-bye", something = "nothing", back = "forwards" } table[2][1] = { hello = "good-bye", something = "nothing", back = "forwards" } table[2][2] = { hello = "good-bye", something = "nothing", back = "forwards" }

Another solution would be to create a function that returns a new instance of your default “nestedTable”, and call that function when repopulating your table:

local function newNested() return { hello = "good-bye", something = "nothing", back = "forwards" } end table[1][1] = newNested() table[1][2] = newNested() table[2][1] = newNested() table[2][2] = newNested()

But here’s another way to accomplish it, and it’s handier because it is re-usable in other parts of your app. Here’s a handy little “makeCopy” function that you can use any time you want to make a copy of an existing variable, regardless of the variable’s type - it’ll always return a distinct duplicate of the original, including subtables:

function makeCopy(original) if original == nil then print("makeCopy Error: No original specified!") return nil end local copy if type(original) == "table" then copy = {} for k,v in pairs(original) do copy[k] = makeCopy(v) end else copy = original end return copy end

So if you include the makeCopy function in your project, then you could modify your code like this to get the desired result:

local nestedTable = { hello = "good-bye", something = "nothing", back = "forwards" } table[1][1] = makeCopy(nestedTable) table[1][2] = makeCopy(nestedTable) table[2][1] = makeCopy(nestedTable) table[2][2] = makeCopy(nestedTable)

Hope this helps! :slight_smile:

Great answer @schroederapps!

But let me add on to this. You might want to avoid naming your table “table” since that’s the name of the table object. You will make things like table.insert(), table.sort() inaccessible as long as your data table named “table” in scope.

Rob

Hi @schroederapps,

Thank you very much for the reply - I agree with Rob that it was a great answer! I implemented the makeCopy() function you provided, and it solved the issue for me. From the way you explained it, I think I understand the logic behind why I was getting the duplicated results before also. It’s definitely an easy issue to get hung up on if you aren’t super familiar with how data assignment works, so I’m glad you clarified it for me.

Also, thanks Rob for the tip about avoiding using “table” as the name of my table variable. I was actually getting errors when experimenting with table.insert and table.remove functions to try and solve my problem, so was planning on posting this as a follow-up question at some point. But, your explanation answered this question for me. Much appreciated!