How to copy Lua table by value?

I recently wrote some Lua code something like this:

local a = {} for i = 1, n do local copy = a -- alter the values in the copy end 

Obviously, this is not what I wanted to do, since the variables contain references to an anonymous table, not the values ​​of the table itself in Lua. This is clearly stated in Programming in Lua , but I forgot about it.

So the question is, what should I write instead of copy = a to get a copy of the values ​​in a ?

+50
lua-table lua
Mar 12 '09 at 21:52
source share
15 answers

To play small, readable code golf, here's a short version that handles standard complex cases:

  • tables as keys,
  • saving metadata and
  • recursive tables.

We can do this in 7 lines:

 function copy(obj, seen) if type(obj) ~= 'table' then return obj end if seen and seen[obj] then return seen[obj] end local s = seen or {} local res = setmetatable({}, getmetatable(obj)) s[obj] = res for k, v in pairs(obj) do res[copy(k, s)] = copy(v, s) end return res end 

At its core, there is a short record of deep copy Lua operations.

Another useful link is this Lua wiki page , which includes an example of how to avoid the __pairs __pairs .

+19
Oct 14 '14 at 17:49
source share

There are many potential definitions in a copy of a table. It depends on whether you want a simple or deep copy, whether you want to copy, share or ignore metadata, etc. There is no single implementation that could satisfy everyone.

One approach is to simply create a new table and duplicate all key / value pairs:

 function table.shallow_copy(t) local t2 = {} for k,v in pairs(t) do t2[k] = v end return t2 end copy = table.shallow_copy(a) 

Note that ipairs should be used instead of ipairs , since ipairs only ipairs over a subset of the table keys (i.e., consecutive positive integer keys starting with one in ascending order).

+42
Mar 13 '09 at 9:51
source share

To illustrate this, my personal table.copy also pays attention to metatables:

 function table.copy(t) local u = { } for k, v in pairs(t) do u[k] = v end return setmetatable(u, getmetatable(t)) end 

There is no copy function widely agreed upon to be called "standard."

+30
Mar 20 '09 at 0:56
source share

The full version of the deep copy that handles all three situations:

  • Circular link table
  • Keys, which are also tables
  • Metastable

General version:

 local function deepcopy(o, seen) seen = seen or {} if o == nil then return nil end if seen[o] then return seen[o] end local no if type(o) == 'table' then no = {} seen[o] = no for k, v in next, o, nil do no[deepcopy(k, seen)] = deepcopy(v, seen) end setmetatable(no, deepcopy(getmetatable(o), seen)) else -- number, string, boolean, etc no = o end return no end 

Or table version:

 function table.deepcopy(o, seen) seen = seen or {} if o == nil then return nil end if seen[o] then return seen[o] end local no = {} seen[o] = no setmetatable(no, deepcopy(getmetatable(o), seen)) for k, v in next, o, nil do k = (type(k) == 'table') and k:deepcopy(seen) or k v = (type(v) == 'table') and v:deepcopy(seen) or v no[k] = v end return no end 

Based on the functions lua-users.org/wiki/CopyTable and Alan Yates .

+12
Apr 18 '13 at 8:07
source share

Optional deep, graph-general, recursive version:

 function table.copy(t, deep, seen) seen = seen or {} if t == nil then return nil end if seen[t] then return seen[t] end local nt = {} for k, v in pairs(t) do if deep and type(v) == 'table' then nt[k] = table.copy(v, deep, seen) else nt[k] = v end end setmetatable(nt, table.copy(getmetatable(t), deep, seen)) seen[t] = nt return nt end 

Perhaps a metathetic copy should also be optional?

+10
Apr 19 '11 at 1:17
source share

Here is what I actually did:

 for j,x in ipairs(a) do copy[j] = x end 

As Doub mentions , if your tables are not strictly monotonously growing, this should be pairs not ipairs .

I also found a deepcopy function that is more robust:

 function deepcopy(orig) local orig_type = type(orig) local copy if orig_type == 'table' then copy = {} for orig_key, orig_value in next, orig, nil do copy[deepcopy(orig_key)] = deepcopy(orig_value) end setmetatable(copy, deepcopy(getmetatable(orig))) else -- number, string, boolean, etc copy = orig end return copy end 

It processes tables and meta tags, invoking itself recursively ( which is its own reward ). One of the smart bits is that you can pass it any value (regardless of the table or not), and it will be copied correctly. However, the cost is that it can potentially overflow the stack. Therefore, an even more robust (non-recursive) function may be required .

But this is an overflow for a very simple case when you want to copy an array to another variable.

+6
Mar 12 '09 at 21:52
source share

The document ( stdlib (unfortunately, easily documented) has a number of valuable extensions for several libraries that come with the standard Lua distribution, including several options for copying and merging tables.

This library is also included in Lua for Windows and probably should be part of any serious Lua toolkit.

One thing to make sure that when implementing such things manually is the proper processing of metadata. For simple table-as-structure applications, you probably don't have any metadata, and a simple loop using pairs() is an acceptable answer. But if the table is used as a tree or contains circular references or has meta tags, then everything becomes more complicated.

+4
Mar 13 '09 at 21:19
source share

Do not forget that functions are also links, therefore, if you want to completely "copy" all the values ​​necessary to obtain individual functions; however, the only way I know to copy a function is to use loadstring(string.dump(func)) , which, according to the Lua reference manual, does not work for functions with upvalues.

 do local function table_copy (tbl) local new_tbl = {} for key,value in pairs(tbl) do local value_type = type(value) local new_value if value_type == "function" then new_value = loadstring(string.dump(value)) -- Problems may occur if the function has upvalues. elseif value_type == "table" then new_value = table_copy(value) else new_value = value end new_tbl[key] = new_value end return new_tbl end table.copy = table_copy end 
+4
Jun 26 '13 at 15:25
source share

This is as good as you get for base tables. Use something like deepcopy if you need to copy tables with meta tags.

+1
Mar 13 '09 at 0:48
source share

I think the reason Lua does not have "table.copy ()" in its standard libraries is because the task is inaccurate to determine. As already shown here, you can either make a “one level of depth” copy (what you did), a deep copy with or without care for possible duplicate links. And then there are metatables.

Personally, I would like them to offer a built-in function. Only if people are not happy with their semantics will they need to do it themselves. Not very often, however, there is actually a need for copying by value.

+1
Mar 15 '09 at 23:05
source share

In most cases, when I needed to copy a table, I wanted to have a copy that has nothing to do with the original, so any modification of the original table does not affect the copy (and vice versa).

All the fragments that have been shown so far cannot create a copy of the table, which may have shared keys or keys with the tables, as they will be left pointing to the original table. It is easy to see if you are trying to copy a table created as: a = {}; a[a] = a a = {}; a[a] = a . deepcopy , which John refers to, will take care of this, so if you need to create a real / full copy, you should use deepcopy .

+1
Jun 10 '12 at 22:20
source share

Warning: the marked solution is WRONG !

When a table contains tables, references to these tables will continue to be used. I was looking for two hours for the error I made, while this was due to using the above code.

So, you need to check if the value is a table or not. If so, you should call table.copy recursively!

This is the correct table.copy function:

 function table.copy(t) local t2 = {}; for k,v in pairs(t) do if type(v) == "table" then t2[k] = table.copy(v); else t2[k] = v; end end return t2; end 

Note. It may also be incomplete if the table contains functions or other special types, but it is possible that most of us do not need. The above code is easily adaptable for those who need it.

+1
Sep 24 '13 at 7:55
source share

Use the penlight library here: https://stevedonovan.imtqy.com/Penlight/api/libraries/pl.tablex.html#deepcopy

 local pl = require 'pl.import_into'() local newTable = pl.tablex.deepcopy(oldTable) 
+1
Jan 31 '16 at 18:29
source share

This may be the easiest way:

 local data = {DIN1 = "Input(z)", DIN2 = "Input(y)", AINA1 = "Input(x)"} function table.copy(mytable) --mytable = the table you need to copy newtable = {} for k,v in pairs(mytable) do newtable[k] = v end return newtable end new_table = table.copy(data) --copys the table "data" 
0
Jun 22 '15 at 14:24
source share

In my situation, when the information in the table is only data and other tables (excluding functions, ...), the following line of code is a winning solution:

 local copyOfTable = json.decode( json.encode( sourceTable ) ) 

I am writing Lua code for some home automation in the Fibaro 2 home center. Lua implementation is very limited without a central library of functions that you can reference. Each function must be declared in the code in order to keep the code working, therefore single-line solutions like this are favorable.

0
02 Oct '16 at 23:31
source share



All Articles