Copy, not direct reference of table?

According to this (great) tutorial, when you set a table equal to another table, it makes a direct reference, not a copy:
http://www.coronalabs.com/blog/2011/07/19/local-variables-lua/

Is there a way to make a copy that doesn’t point to the original so that I can work with the data without updating the actual reference table?

I’ve thought of a few scripted approaches but this seems like there should be an easy syntax based solution that doesn’t require a programmatic effort!

Thanks :slight_smile:
[import]uid: 105707 topic_id: 32957 reply_id: 332957[/import]

Something like this?

function deepCopy(object) local lookup\_table = {} local function \_copy(object) if type(object) ~= "table" then return object elseif lookup\_table[object] then return lookup\_table[object] end local new\_table = {} lookup\_table[object] = new\_table for index, value in pairs(object) do new\_table[\_copy(index)] = \_copy(value) end return setmetatable(new\_table, getmetatable(object)) end return \_copy(object) end [import]uid: 79933 topic_id: 32957 reply_id: 130891[/import]

One way or another, you need to program a few lines (at least) to duplicate a table. If the table is just a simple series of un-indexed (unnamed) items, you can just loop through the source table by count and copy each item into a new blank table. If the table is a series of indexes, you’ll need to loop through by “pairs”. If it’s a mix of both, it gets a fair bit more complicated. :slight_smile:

Brent [import]uid: 9747 topic_id: 32957 reply_id: 130897[/import]

Something like this?

function deepCopy(object) local lookup\_table = {} local function \_copy(object) if type(object) ~= "table" then return object elseif lookup\_table[object] then return lookup\_table[object] end local new\_table = {} lookup\_table[object] = new\_table for index, value in pairs(object) do new\_table[\_copy(index)] = \_copy(value) end return setmetatable(new\_table, getmetatable(object)) end return \_copy(object) end [import]uid: 79933 topic_id: 32957 reply_id: 130891[/import]

One way or another, you need to program a few lines (at least) to duplicate a table. If the table is just a simple series of un-indexed (unnamed) items, you can just loop through the source table by count and copy each item into a new blank table. If the table is a series of indexes, you’ll need to loop through by “pairs”. If it’s a mix of both, it gets a fair bit more complicated. :slight_smile:

Brent [import]uid: 9747 topic_id: 32957 reply_id: 130897[/import]

That function in the post above is a recursive table copier, like Brent said.

If the table is just a simple series of un-indexed (unnamed) items, it will loop through the source table by count and copy each item into a new blank table. If the table is a series of indexes/tables, it will loop through by “pairs”. If it’s a mix of both, it is recursive and calls itself to handle all items appropriately.

Oh, and it sets the metatable too :wink: [import]uid: 79933 topic_id: 32957 reply_id: 131129[/import]

mpappas and Brent,

I appreciate your input. So the short answer is that it seems there’s no way to copy a table short of programming a solution?

The tables I’m looking at copying are unindexed (no keys) but what does make it less than totally straightforward is that they are matrixes (arrays of arrays).

I’m using these “tables of tables” to store cell data for top down tile based level information. Here’s an abbreviated example (smaller than what I’m using) where "1"s are “walls” and "0"s are “open”:

[lua]local map = {
{1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 1, 0, 0, 1},
{1, 0, 1, 0, 1, 0, 0, 1},
{1, 0, 1, 0, 0, 0, 0, 1},
{1, 0, 1, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1}
}[/lua]

mpappas, the function you’ve shared strikes me as elegant and as a very handy tool. Although I’ve been getting much more capable with Lua, I’m still getting my head around using metatables. I’m going to study up so I can better understand the functionality you’ve shared. I think this is something that will prove very helpful as I continue my efforts :slight_smile:
Here is what I’ve whipped up specific to making copies of my map matrixes. This is of course limited to my maps but I’ve checked it out and it seems to have solved the problem for me:
[lua]local function copyMap(passedMatrix)
local matrixCopy = {}
for y = 1, #passedMatrix do
matrixCopy[y] = {}
for x = 1, #passedMatrix[1] do
matrixCopy[y][x] = passedMatrix[y][x]
end
end
return matrixCopy
end

local mapCopy = copyMap(map)[/lua]

Thanks again for the help, I welcome any more input anyone has :slight_smile: [import]uid: 105707 topic_id: 32957 reply_id: 131135[/import]

Hi EHO,
Yep, short answer is that you need to program a table copy routine. It’s not built in to Lua.

http://lua-users.org/wiki/CopyTable

For your case, you could still use my “simple” approach if you want, it would just involve embedding one “for” loop inside another “for” loop, to duplicate the contents of each internal table in addition to the table itself. Not complicated really, just depends on if you want to implement the fail-safe method that @mpappas provided.

Brent
[import]uid: 9747 topic_id: 32957 reply_id: 131139[/import]

@mpappas - thanks for sharing the insight on your function! I missed your second post as I was working on my response sharing my basic, “table of tables” specific solution.

Like I said, your solution strikes me as elegant, and is something I’m likely to use/refer to in the future. I now see the recursive element since you mentioned that you had used that type of approach. Because I’m using these tables of tables, would I need an extra “level” to the function to address that or will this still work that out? Thinking through this is really giving me a brain workout which is good because I’m learning. Thanks :slight_smile:

[import]uid: 105707 topic_id: 32957 reply_id: 131142[/import]

@EHO

I just did a quick test on your data, and the deepcopy works just fine. Using a couple utility functions, it’s pretty easy to verify…

-- First a couple utility functions...  
--  
-- Creates a complete/deep copy of the data  
function deepCopy(object)  
 local lookup\_table = {}  
 local function \_copy(object)  
 if type(object) ~= "table" then  
 return object  
 elseif lookup\_table[object] then  
 return lookup\_table[object]  
 end  
 local new\_table = {}  
 lookup\_table[object] = new\_table  
 for index, value in pairs(object) do  
 new\_table[\_copy(index)] = \_copy(value)  
 end  
 return setmetatable(new\_table, getmetatable(object))  
 end  
 return \_copy(object)  
end  
  
--  
-- Giant table printing function... deals with recursion and stuffs...  
--  
function table.show(t, name, indent)  
 local cart -- a container  
 local autoref -- for self references  
  
 local function isemptytable(t) return next(t) == nil end  
  
 local function basicSerialize (o)  
 local so = tostring(o)  
 if type(o) == "function" then  
 local info = debug.getinfo(o, "S")  
 -- info.name is nil because o is not a calling level  
 if info.what == "C" then  
 return string.format("%q", so .. ", C function")  
 else   
 -- the information is defined through lines  
 return string.format("%q", so .. ", defined in (" ..  
 info.linedefined .. "-" .. info.lastlinedefined ..  
 ")" .. info.source)  
 end  
 elseif type(o) == "number" or type(o) == "boolean" then  
 return so  
 else  
 return string.format("%q", so)  
 end  
 end  
  
 local function addtocart (value, name, indent, saved, field)  
 indent = indent or ""  
 saved = saved or {}  
 field = field or name  
  
 cart = cart .. indent .. field  
  
 if type(value) ~= "table" then  
 cart = cart .. " = " .. basicSerialize(value) .. ";\n"  
 else  
 if saved[value] then  
 cart = cart .. " = {}; -- " .. saved[value]   
 .. " (self reference)\n"  
 autoref = autoref .. name .. " = " .. saved[value] .. ";\n"  
 else  
 saved[value] = name  
 --if tablecount(value) == 0 then  
 if isemptytable(value) then  
 cart = cart .. " = {};\n"  
 else  
 cart = cart .. " = {\n"  
 for k, v in pairs(value) do  
 k = basicSerialize(k)  
 local fname = string.format("%s[%s]", name, k)  
 field = string.format("[%s]", k)  
 -- three spaces between levels  
 addtocart(v, fname, indent .. " ", saved, field)  
 end  
 cart = cart .. indent .. "};\n"  
 end  
 end  
 end  
 end  
  
 name = name or "\_\_unnamed\_\_"  
 if type(t) ~= "table" then  
 return name .. " = " .. basicSerialize(t)  
 end  
 cart, autoref = "", ""  
 addtocart(t, name, indent)  
 return cart .. autoref  
end  
  
--  
-- Top level call to print a table...  
--  
function printTable( t, label )  
  
 print(table.show(t, label))   
  
end   
  
--------------------------  
  
local map = {  
{1, 1, 1, 1, 1, 1, 1, 1},  
{1, 0, 0, 0, 1, 0, 0, 1},  
{1, 0, 1, 0, 1, 0, 0, 1},  
{1, 0, 1, 0, 0, 0, 0, 1},  
{1, 0, 1, 0, 0, 0, 0, 1},  
{1, 1, 1, 1, 1, 1, 1, 1}  
}  
  
local map2 = deepCopy(map) -- This is a complete copy (not a reference...)   
  
map2[1][1] = 9 -- Test values, should ONLY be changed in map2 (the deepCopy)  
map2[1][2] = 9  
map2[1][3] = 9   
  
printTable( map, "Original Map")  
  
printTable( map2, "deepCopy of Map")  
print("Done.")  

Best of luck. [import]uid: 79933 topic_id: 32957 reply_id: 131143[/import]

Hi Brent,

Did you see my solution above? I think I approached this with the “simple” approach you had mentioned. :slight_smile:

That link is very helpful, I need to better explore the lua users wiki! For as much as I’ve been pouring myself into Corona and Lua, I’ve not made enough use of that resource. Thanks for pointing that out! [import]uid: 105707 topic_id: 32957 reply_id: 131145[/import]

Someone correct me if I am wrong but if you are using the JSON library or don’t mind using it, a very simple way to copy a table would be the following.

[code]

require “json”

local map = {
{1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 1, 0, 0, 1},
{1, 0, 1, 0, 1, 0, 0, 1},
{1, 0, 1, 0, 0, 0, 0, 1},
{1, 0, 1, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1}
}

local jCopy = json.encode(map)

local copyOfMap = {}
copyOfMap = json.decode(jCopy)

[/code] [import]uid: 56820 topic_id: 32957 reply_id: 131146[/import]

@mpappas

Good lord you whipped that up quick. I was going to give it a go and you already had this up! As they say… “Siiiiiiiiick!”

I just tried your code out. I’ve been wrestling with figuring out how to print the contents of my arrays for a long time. That print function is insanely useful for me :slight_smile:

Very neat to see that the deepCopy function works as needed. Thank you :slight_smile:

[import]uid: 105707 topic_id: 32957 reply_id: 131147[/import]

@anderoth, yes, that should work too. It should work great for light duty stuff (under a 100kb, not in loops, etc)

There’s more overhead encoding the data into/out of the json string format, but it would definitely return a new structure, and be just fine for a lot of cases.

@EHO, ehh, it was just a couple utility functions I had laying around :wink: Enjoy. [import]uid: 79933 topic_id: 32957 reply_id: 131148[/import]

@anderoth,

You’ve hit on something important for me…

I’ve not explored the save/load aspect of my project. I will need to have that capability for my map tables.

Do you use the json API or another solution for saving/loading data in your projects?
[import]uid: 105707 topic_id: 32957 reply_id: 131150[/import]

That function in the post above is a recursive table copier, like Brent said.

If the table is just a simple series of un-indexed (unnamed) items, it will loop through the source table by count and copy each item into a new blank table. If the table is a series of indexes/tables, it will loop through by “pairs”. If it’s a mix of both, it is recursive and calls itself to handle all items appropriately.

Oh, and it sets the metatable too :wink: [import]uid: 79933 topic_id: 32957 reply_id: 131129[/import]

mpappas and Brent,

I appreciate your input. So the short answer is that it seems there’s no way to copy a table short of programming a solution?

The tables I’m looking at copying are unindexed (no keys) but what does make it less than totally straightforward is that they are matrixes (arrays of arrays).

I’m using these “tables of tables” to store cell data for top down tile based level information. Here’s an abbreviated example (smaller than what I’m using) where "1"s are “walls” and "0"s are “open”:

[lua]local map = {
{1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 1, 0, 0, 1},
{1, 0, 1, 0, 1, 0, 0, 1},
{1, 0, 1, 0, 0, 0, 0, 1},
{1, 0, 1, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1}
}[/lua]

mpappas, the function you’ve shared strikes me as elegant and as a very handy tool. Although I’ve been getting much more capable with Lua, I’m still getting my head around using metatables. I’m going to study up so I can better understand the functionality you’ve shared. I think this is something that will prove very helpful as I continue my efforts :slight_smile:
Here is what I’ve whipped up specific to making copies of my map matrixes. This is of course limited to my maps but I’ve checked it out and it seems to have solved the problem for me:
[lua]local function copyMap(passedMatrix)
local matrixCopy = {}
for y = 1, #passedMatrix do
matrixCopy[y] = {}
for x = 1, #passedMatrix[1] do
matrixCopy[y][x] = passedMatrix[y][x]
end
end
return matrixCopy
end

local mapCopy = copyMap(map)[/lua]

Thanks again for the help, I welcome any more input anyone has :slight_smile: [import]uid: 105707 topic_id: 32957 reply_id: 131135[/import]

Hi EHO,
Yep, short answer is that you need to program a table copy routine. It’s not built in to Lua.

http://lua-users.org/wiki/CopyTable

For your case, you could still use my “simple” approach if you want, it would just involve embedding one “for” loop inside another “for” loop, to duplicate the contents of each internal table in addition to the table itself. Not complicated really, just depends on if you want to implement the fail-safe method that @mpappas provided.

Brent
[import]uid: 9747 topic_id: 32957 reply_id: 131139[/import]

@mpappas - thanks for sharing the insight on your function! I missed your second post as I was working on my response sharing my basic, “table of tables” specific solution.

Like I said, your solution strikes me as elegant, and is something I’m likely to use/refer to in the future. I now see the recursive element since you mentioned that you had used that type of approach. Because I’m using these tables of tables, would I need an extra “level” to the function to address that or will this still work that out? Thinking through this is really giving me a brain workout which is good because I’m learning. Thanks :slight_smile:

[import]uid: 105707 topic_id: 32957 reply_id: 131142[/import]

@EHO

I just did a quick test on your data, and the deepcopy works just fine. Using a couple utility functions, it’s pretty easy to verify…

-- First a couple utility functions...  
--  
-- Creates a complete/deep copy of the data  
function deepCopy(object)  
 local lookup\_table = {}  
 local function \_copy(object)  
 if type(object) ~= "table" then  
 return object  
 elseif lookup\_table[object] then  
 return lookup\_table[object]  
 end  
 local new\_table = {}  
 lookup\_table[object] = new\_table  
 for index, value in pairs(object) do  
 new\_table[\_copy(index)] = \_copy(value)  
 end  
 return setmetatable(new\_table, getmetatable(object))  
 end  
 return \_copy(object)  
end  
  
--  
-- Giant table printing function... deals with recursion and stuffs...  
--  
function table.show(t, name, indent)  
 local cart -- a container  
 local autoref -- for self references  
  
 local function isemptytable(t) return next(t) == nil end  
  
 local function basicSerialize (o)  
 local so = tostring(o)  
 if type(o) == "function" then  
 local info = debug.getinfo(o, "S")  
 -- info.name is nil because o is not a calling level  
 if info.what == "C" then  
 return string.format("%q", so .. ", C function")  
 else   
 -- the information is defined through lines  
 return string.format("%q", so .. ", defined in (" ..  
 info.linedefined .. "-" .. info.lastlinedefined ..  
 ")" .. info.source)  
 end  
 elseif type(o) == "number" or type(o) == "boolean" then  
 return so  
 else  
 return string.format("%q", so)  
 end  
 end  
  
 local function addtocart (value, name, indent, saved, field)  
 indent = indent or ""  
 saved = saved or {}  
 field = field or name  
  
 cart = cart .. indent .. field  
  
 if type(value) ~= "table" then  
 cart = cart .. " = " .. basicSerialize(value) .. ";\n"  
 else  
 if saved[value] then  
 cart = cart .. " = {}; -- " .. saved[value]   
 .. " (self reference)\n"  
 autoref = autoref .. name .. " = " .. saved[value] .. ";\n"  
 else  
 saved[value] = name  
 --if tablecount(value) == 0 then  
 if isemptytable(value) then  
 cart = cart .. " = {};\n"  
 else  
 cart = cart .. " = {\n"  
 for k, v in pairs(value) do  
 k = basicSerialize(k)  
 local fname = string.format("%s[%s]", name, k)  
 field = string.format("[%s]", k)  
 -- three spaces between levels  
 addtocart(v, fname, indent .. " ", saved, field)  
 end  
 cart = cart .. indent .. "};\n"  
 end  
 end  
 end  
 end  
  
 name = name or "\_\_unnamed\_\_"  
 if type(t) ~= "table" then  
 return name .. " = " .. basicSerialize(t)  
 end  
 cart, autoref = "", ""  
 addtocart(t, name, indent)  
 return cart .. autoref  
end  
  
--  
-- Top level call to print a table...  
--  
function printTable( t, label )  
  
 print(table.show(t, label))   
  
end   
  
--------------------------  
  
local map = {  
{1, 1, 1, 1, 1, 1, 1, 1},  
{1, 0, 0, 0, 1, 0, 0, 1},  
{1, 0, 1, 0, 1, 0, 0, 1},  
{1, 0, 1, 0, 0, 0, 0, 1},  
{1, 0, 1, 0, 0, 0, 0, 1},  
{1, 1, 1, 1, 1, 1, 1, 1}  
}  
  
local map2 = deepCopy(map) -- This is a complete copy (not a reference...)   
  
map2[1][1] = 9 -- Test values, should ONLY be changed in map2 (the deepCopy)  
map2[1][2] = 9  
map2[1][3] = 9   
  
printTable( map, "Original Map")  
  
printTable( map2, "deepCopy of Map")  
print("Done.")  

Best of luck. [import]uid: 79933 topic_id: 32957 reply_id: 131143[/import]