Copy Table |
|
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:
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
__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
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.