Module:table: Difference between revisions

no edit summary
No edit summary
No edit summary
 
Line 88: Line 88:
return next, t
return next, t
end
end
 
local function make_copy(orig, memo, mt_flag, keep_loaded_data)
local function make_copy(orig, memo, mt_flag, keep_loaded_data)
if type(orig) ~= "table" then
if type(orig) ~= "table" then
Line 117: Line 117:
return copy
return copy
end
end
 
--[==[
--[==[
Recursive deep copy function. Preserves copied identities of subtables.
Recursive deep copy function. Preserves copied identities of subtables.
Line 391: Line 391:
return next(tablekeys_b) == nil
return next(tablekeys_b) == nil
end
end
 
--[==[
--[==[
Recursively compare two values that may be tables, and returns true if all key-value pairs are structurally equivalent. Note that this handles arbitrary nesting of subtables (including recursive nesting) to any depth, for keys as well as values.
Recursively compare two values that may be tables, and returns true if all key-value pairs are structurally equivalent. Note that this handles arbitrary nesting of subtables (including recursive nesting) to any depth, for keys as well as values.
Line 410: Line 410:
return a[b]
return a[b]
end
end
 
--[==[
--[==[
Given a table and an arbitrary number of keys, will successively access subtables using each key in turn, returning the value at the final key. For example, if {t} is { {[1] = {[2] = {[3] = "foo"}}}}, {export.getNested(t, 1, 2, 3)} will return {"foo"}.
Given a table and an arbitrary number of keys, will successively access subtables using each key in turn, returning the value at the final key. For example, if {t} is { {[1] = {[2] = {[3] = "foo"}}}}, {export.getNested(t, 1, 2, 3)} will return {"foo"}.
 
If no subtable exists for a given key value, returns nil, but will throw an error if a non-table is found at an intermediary key.
If no subtable exists for a given key value, returns nil, but will throw an error if a non-table is found at an intermediary key.
]==]
]==]
Line 437: Line 437:
return set_nested(t, b, ...)
return set_nested(t, b, ...)
end
end
 
--[==[
--[==[
Given a table, value and an arbitrary number of keys, will successively access subtables using each key in turn, and sets the value at the final key. For example, if {t} is { {} }, {export.setNested(t, "foo", 1, 2, 3)} will modify {t} to { {[1] = {[2] = {[3] = "foo"} } } }.
Given a table, value and an arbitrary number of keys, will successively access subtables using each key in turn, and sets the value at the final key. For example, if {t} is { {} }, {export.setNested(t, "foo", 1, 2, 3)} will modify {t} to { {[1] = {[2] = {[3] = "foo"} } } }.
 
If no subtable exists for a given key value, one will be created, but the function will throw an error if a non-table value is found at an intermediary key.
If no subtable exists for a given key value, one will be created, but the function will throw an error if a non-table value is found at an intermediary key.
 
Note: the parameter order (table, value, keys) differs from functions like rawset, because the number of keys can be arbitrary. This is to avoid situations where an additional argument must be appended to arbitrary lists of variables, which can be awkward and error-prone: for example, when handling variable arguments ({{lua|...}}) or function return values.
Note: the parameter order (table, value, keys) differs from functions like rawset, because the number of keys can be arbitrary. This is to avoid situations where an additional argument must be appended to arbitrary lists of variables, which can be awkward and error-prone: for example, when handling variable arguments ({{lua|...}}) or function return values.
]==]
]==]
Line 492: Line 492:


--[==[
--[==[
Given a `list` and an `item` to be inserted, append the value to the end of the list if not already present
Given a `list` and a `new_item` to be inserted, append the value to the end of the list if not already present
(or insert at an arbitrary position, if `options.pos` is given; see below). Comparison is by value, using {deepEquals}.
(or insert at an arbitrary position, if `options.pos` is given; see below). Comparison is by value, using {deepEquals}.


Line 499: Line 499:
* `pos`: Position at which insertion happens (i.e. before the existing item at position `pos`).
* `pos`: Position at which insertion happens (i.e. before the existing item at position `pos`).
* `key`: Function of one argument to return a comparison key, as with {deepEquals}. The key function is applied to both
* `key`: Function of one argument to return a comparison key, as with {deepEquals}. The key function is applied to both
        `item` and the existing item in `list` to compare against, and the comparison is done against the results.
`item` and the existing item in `list` to compare against, and the comparison is done against the results.
        This is useful when inserting a complex structure into an existing list while avoiding duplicates.
This is useful when inserting a complex structure into an existing list while avoiding duplicates.
* `combine`: Function of three arguments (the existing item, the new item and the position, respectively) to combine an
existing item with `new_item`, when `new_item` is found in `list`. If unspecified, the existing item is
left alone.
 
Return {false} if entry already found, {true} if inserted.


For compatibility, `pos` can be specified directly as the third argument in place of `options`, but this is not
For compatibility, `pos` can be specified directly as the third argument in place of `options`, but this is not
Line 510: Line 515:
checking for duplicates, and use {removeDuplicates()} at the end.)
checking for duplicates, and use {removeDuplicates()} at the end.)
]==]
]==]
function export.insertIfNot(list, item, options)
function export.insertIfNot(list, new_item, options)
local check = _check("insertIfNot")
local check = _check("insertIfNot")
check(1, list, "table")
check(1, list, "table")
Line 518: Line 523:
options = {pos = options}
options = {pos = options}
end
end
if not export.contains(list, item, options) then
if options and options.combine then
if options and options.pos then
local new_item_key
insert(list, options.pos, item)
-- Don't use options.key and options.key(new_item) or new_item in case the key is legitimately false or nil.
if options.key then
new_item_key = options.key(new_item)
else
else
insert(list, item)
new_item_key = new_item
end
for i, item in ipairs(list) do
local item_key
if options.key then
item_key = options.key(item)
else
item_key = item
end
if export.deepEquals(item_key, new_item_key) then
local retval = options.combine(item, new_item, i)
if retval ~= nil then
list[i] = retval
end
return false
end
end
end
elseif export.contains(list, new_item, options) then
return false
end
if options and options.pos then
insert(list, options.pos, new_item)
else
insert(list, new_item)
end
end
end
end
Line 541: Line 570:
check(1, t, "table")
check(1, t, "table")
check(2, valueToFind, {"string", "number"})
check(2, valueToFind, {"string", "number"})
 
for key, value in pairs(t) do
for key, value in pairs(t) do
if value == valueToFind then
if value == valueToFind then
Line 547: Line 576:
end
end
end
end
 
return nil
return nil
end
end
Line 562: Line 591:
return string_sort(key1, key2)
return string_sort(key1, key2)
end
end
 
--[==[
--[==[
Return a list of the keys in a table, sorted using either the default table.sort function or a custom keySort function.
Return a list of the keys in a table, sorted using either the default table.sort function or a custom keySort function.
Line 573: Line 602:
check(2, keySort, "function", true)
check(2, keySort, "function", true)
end
end
 
local list, i = {}, 0
local list, i = {}, 0
for key in pairs(t) do
for key in pairs(t) do
Line 579: Line 608:
list[i] = key
list[i] = key
end
end
 
-- Use specified sort function, or otherwise defaultKeySort.
-- Use specified sort function, or otherwise defaultKeySort.
sort(list, keySort or defaultKeySort)
sort(list, keySort or defaultKeySort)
 
return list
return list
end
end
Line 596: Line 625:
check(1, t, "table")
check(1, t, "table")
check(2, keySort, "function", true)
check(2, keySort, "function", true)
 
local list, i = keys_to_list(t, keySort, true), 0
local list, i = keys_to_list(t, keySort, true), 0
 
return function()
return function()
i = i + 1
i = i + 1
Line 615: Line 644:
end
end
end
end
 
function export.reverseIpairs(t)
function export.reverseIpairs(t)
checkType("reverseIpairs", 1, t, "table")
checkType("reverseIpairs", 1, t, "table")
Line 771: Line 800:
check(1, seq)
check(1, seq)
check(2, options, true)
check(2, options, true)
 
local length = #seq
local length = #seq
 
if not options then
if not options then
options = {}
options = {}
end
end
 
local conj
local conj
if length > 1 then
if length > 1 then
Line 785: Line 814:
end
end
end
end
 
if length == 0 then
if length == 0 then
return ""
return ""
Line 807: Line 836:
function export.sparseConcat(t, sep, i, j)
function export.sparseConcat(t, sep, i, j)
local list = {}
local list = {}
 
local list_i = 0
local list_i = 0
for _, v in export.sparseIpairs(t) do
for _, v in export.sparseIpairs(t) do
Line 813: Line 842:
list[list_i] = v
list[list_i] = v
end
end
 
return concat(list, sep, i, j)
return concat(list, sep, i, j)
end
end
Line 842: Line 871:
function export.invert(array)
function export.invert(array)
checkType("invert", 1, array, "table")
checkType("invert", 1, array, "table")
 
local map = {}
local map = {}
for i, v in ipairs(array) do
for i, v in ipairs(array) do
map[v] = i
map[v] = i
end
end
 
return map
return map
end
end
Line 891: Line 920:
function export.isArray(t)
function export.isArray(t)
checkType("isArray", 1, t, "table")
checkType("isArray", 1, t, "table")
 
local i = 0
local i = 0
for _ in pairs(t) do
for _ in pairs(t) do