45,645
edits
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 | 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. | |||
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, | 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 | if options and options.combine then | ||
if | local new_item_key | ||
-- 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 | ||
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 |