Copy Table


The Lua standard libraries do not provide a function to copy a table. However, it is relatively simple to implement such a function.

A generic table.copy function is not guaranteed to suit all use-cases, as there are many different aspects which must be selected for the specific situation. For example: should metatables be shared or copied? Should we check userdata for a __copy metamethod? These questions (as well as many others) must be asked and answered by the developer.

Some of the Lua "extended standard libraries", such as Penlight and stdlib provide ready-made copy functions for convenience. Check if they suit your use-case before implementing your own.

The following functions provide a base to work off of:

Quick & Dirty

function table.clone(org)
 return {table.unpack(org)}
end
local abc = {5,12,1}
local def = table.clone(abc)
table.sort(def)
print(abc[2], def[2]) -- 12	5

Shallow Copy

This is a simple, naive implementation. It only copies the top level value and its direct children; there is no handling of deeper children, metatables or special types such as userdata or coroutines. It is also susceptible to influence by the __pairs metamethod.

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

Deep Copy

A deep copy copies all levels (or a specific subset of levels).

Here is a simple recursive implementation that additionally handles metatables and avoids the __pairs metamethod.

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

With a few slight edits to the code, it is also possible to create a version of the deepcopy function that works with recursive tables. This is done by creating a table of tables that have been copied and feeding it as a second argument to the deepcopy function.

-- Save copied tables in `copies`, indexed by original table.
function deepcopy(orig, copies)
 copies = copies or {}
 local orig_type = type(orig)
 local copy
 if orig_type == 'table' then
 if copies[orig] then
 copy = copies[orig]
 else
 copy = {}
 copies[orig] = copy
 for orig_key, orig_value in next, orig, nil do
 copy[deepcopy(orig_key, copies)] = deepcopy(orig_value, copies)
 end
 setmetatable(copy, deepcopy(getmetatable(orig), copies))
 end
 else -- number, string, boolean, etc
 copy = orig
 end
 return copy
end

It is important that only one argument is supplied to this version of the deepcopy function. Otherwise, it will attempt to use the second argument as a table, which can have unintended consequences.

Additionally, since these functions are recursive, using them to copy very deep tables may overflow the stack.

Non-recursive Deep Copy

A more flexible (non-recursive) deepcopy implementation is available from [this GitHub gist]. It allows for varying rules on how to copy special types, metatables, and function upvalues (including joining). Please see the comments for usage.
RecentChanges · preferences
edit · history
Last edited December 6, 2022 4:07 am GMT (diff)

AltStyle によって変換されたページ (->オリジナル) /