Module:array
- The following documentation is located at Module:array/documentation. [edit]
- Useful links: subpage list • links • transclusions • testcases • sandbox (diff)
Returns an array constructor that produces a table that has a number of functions available as methods: the table
library functions, and various vanilla Lua functions and functions from Module:table and Module:fun that operate on arrays or on tables with integer keys.
local Array = require("Module:array")
local nums = Array() -- or Array:new()
nums:type() --> "array"
for i = 1, 5 do
nums:insert(i)
end
nums:concat(", ") --> "1, 2, 3, 4, 5"
local squares = nums:map(function (num) return num ^ 2 end) -- Returns new array.
squares:concat(', ') --> "1, 4, 9, 16, 25"
local even_squares = squares:filter(function (square) return square % 2 == 0 end)
even_squares:concat(", ") --> "4, 16"
The functions from Module:table and Module:fun are loaded as needed.
Functions from Module:table:
compressSparseArray
(aliascompress
),contains
,invert
,isArray
,length
,listToSet
(aliastoSet
),maxIndex
,numKeys
,removeDuplicates
,reverse
,reverseIpairs
,serialCommaJoin
,sparseIpairs
Functions from Module:fun. These have a function as the second argument (first argument of method):
all
,filter
,fold
,map
,some
These functions are included in a funcs
subtable of an array (awkward):
keysToList
,numKeys
The following functions return an array (with the array metatable):
compressSparseArray
,keysToList
,numKeys
,removeDuplicates
,reverse
The names with underscores instead of camel case can be used as aliases: for instance, arr:to_set()
instead of arr:toSet()
.
The array constructor behaves differently depending on the arguments supplied to it. Without arguments, it creates an empty table. Given a single table, it adds the metatable to it. If the table has been loaded with mw.loadData
, it duplicates the table, removing the metatable that is found in tables loaded with mw.loadData
. Otherwise, it creates a new table (array) containing the arguments.
The array constructor does this by adding a metatable. This is similar to how all strings have a metatable that allows the string
library functions to be used as methods: for instance, ("abc"):sub(1, 1)
for string.sub("abc", 1, 1)
.
local export = {}
local debug_track_module = "Module:debug/track"
local function_module = "Module:fun"
local table_module = "Module:table"
local get_array_mt -- Defined below.
local getmetatable = getmetatable
local ipairs = ipairs
local pairs = pairs
local rawget = rawget
local rawset = rawset
local require = require
local select = select
local setmetatable = setmetatable
local sort = table.sort
local type = type
local upper = string.upper
local array_mt -- Defined below.
--[==[
Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==]
local function append(...)
append = require(table_module).append
return append(...)
end
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function deep_copy(...)
deep_copy = require(table_module).deepCopy
return deep_copy(...)
end
local function list_to_set(...)
list_to_set = require(table_module).listToSet
return list_to_set(...)
end
local function shallow_copy(...)
shallow_copy = require(table_module).shallowCopy
return shallow_copy(...)
end
--[==[
Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==]
local m_function
local function get_m_function()
m_function, get_m_function = require(function_module), nil
return m_function
end
local m_table
local function get_m_table()
m_table, get_m_table = require(table_module), nil
return m_table
end
-- Functions from [[Module:table]] that operate on arrays or sparse arrays.
-- List copied from [[Module:table/documentation]].
local m_table_array_funcs
local function get_m_table_array_funcs()
m_table_array_funcs = list_to_set{
-- non-sparse
"signedIndex", "append", "extend", "slice", "removeDuplicates", "length",
"size", "contains", "serialCommaJoin", "reverseIpairs", "reverse",
"invert", "listToSet", "flatten", "isArray",
-- sparse
"numKeys", "maxIndex", "compressSparseArray", "indexPairs", "indexIpairs",
"sparseIpairs",
-- tables in general
"shallowCopy", "deepCopy"
}
get_m_table_array_funcs = nil
return m_table_array_funcs
end
-- Functions from [[Module:fun]] that take an array in the second argument.
-- They just have to have the argument order reversed to work as methods of the
-- array object.
local m_function_array_funcs
local function get_m_function_array_funcs()
m_function_array_funcs = list_to_set{
"map", "some", "all", "filter", "fold"
}
get_m_function_array_funcs = nil
return m_function_array_funcs
end
-- Functions from [[Module:table]] that create an array or table.
-- Not all of these operate on arrays.
local m_table_new_array_funcs
local function get_m_table_new_array_funcs()
m_table_new_array_funcs = list_to_set{
-- Array.
"append", "slice", "removeDuplicates", "numKeys", "compressSparseArray",
"keysToList", "reverse", "flatten",
-- Array or table.
"shallowCopy", "deepCopy"
}
get_m_table_new_array_funcs = nil
return m_table_new_array_funcs
end
-- Functions from [[Module:fun]] that create an array or table.
-- Not all of these operate on arrays.
local m_function_new_array_funcs
local function get_m_function_new_array_funcs()
m_function_new_array_funcs = list_to_set{
"map", "filter",
}
get_m_function_new_array_funcs = nil
return m_function_new_array_funcs
end
-- Add aliases for the functions from [[Module:table]] whose names
-- contain "array" or "list", which is redundant.
-- The key redirects to the value.
local alias_of = {
compress = "compressSparseArray",
keys = "keysToList",
toSet = "listToSet",
}
local function underscore_to_camel_case(str)
if type(str) ~= "string" then
return str
end
local ret = str:gsub("_(.)", upper)
if ret ~= str then
debug_track("array/underscore to camel case")
end
return ret
end
local function get_module_function(key, module, module_name)
return module[key] or
error(("Cannot find %s in [[Module:%s]]"):format(mw.dumpObject(key), module_name))
end
local function wrap_in_array_constructor(func)
return function (...)
return setmetatable(func(...), array_mt or get_array_mt())
end
end
function get_array_mt()
-- Copy table library so as not to unexpectedly change the behavior of code that
-- uses it.
local Array = deep_copy(table)
Array.ipairs = ipairs
Array.pairs = pairs
Array.unpack = unpack
Array.listToText = mw.text.listToText
-- Create version of table.sort that returns the table.
function Array:sort(comp)
sort(self, comp)
return self
end
function Array:type()
local mt = getmetatable(self)
return mt and type(mt) == "table" and rawget(mt, "__type") or nil
end
local Array_mt = {}
setmetatable(Array, Array_mt)
function Array_mt:__index(key)
if type(key) ~= "string" then
return nil
end
-- Convert underscores to camel case: num_keys -> numKeys.
-- FIXME: this is pointless overhead: remove once nothing relies on it.
key = underscore_to_camel_case(key)
key = alias_of[key] or key
local func = rawget(self, key)
if func ~= nil then
return func
elseif (m_table_array_funcs or get_m_table_array_funcs())[key] then
func = get_module_function(key, m_table or get_m_table(), "table")
if (m_table_new_array_funcs or get_m_table_new_array_funcs())[key] then
func = wrap_in_array_constructor(func)
end
elseif (m_function_array_funcs or get_m_function_array_funcs())[key] then
local raw_func = get_module_function(key, m_function or get_m_function(), "fun")
--[==[ Once isArray is no longer used:
function func(a, b, ...)
return raw_func(b, a, ...)
end
]==]
if key == "fold" then
function func(t, f, accum)
return raw_func(f, t, accum)
end
else
function func(a, b) -- TODO: isArray parameter is probably unnecessary, and doesn't work with sparse arrays anyway.
debug_track("array/isArray")
return raw_func(b, a, "isArray")
end
end
if (m_function_new_array_funcs or get_m_function_new_array_funcs())[key] then
func = wrap_in_array_constructor(func)
end
else
return nil
end
rawset(Array, key, func)
return func
end
array_mt = {
__index = Array,
__type = "array",
}
function array_mt:__add(v)
return setmetatable(append(self, v), array_mt or get_array_mt())
end
get_array_mt = nil
return array_mt
end
-- A function to convert string key-table modules such
-- as [[Module:languages/data/2]] into arrays.
-- "from" is a bad name.
-- field_for_key supplies the field name in which the
-- key will be stored.
function export.from(map, field_for_key)
local arr, i = {}, 0
for key, val in pairs(map) do
i = i + 1
local new_val = shallow_copy(val)
if field_for_key then
new_val[field_for_key] = key
end
arr[i] = new_val
end
return setmetatable(arr, array_mt or get_array_mt())
end
local export_mt = {}
function export_mt:__call(...)
local arr
if select("#", ...) == 1 and type((...)) == "table" then
arr = ...
local mt = getmetatable(arr)
-- If table has been loaded with mw.loadData, copy it to avoid the
-- limitations of it being a virtual table.
if mt and type(mt) == "table" and rawget(mt, "mw_loadData") == true then
arr = shallow_copy(arr)
end
else
arr = {...}
end
return setmetatable(arr, array_mt or get_array_mt())
end
function export_mt:__index(key)
-- Convert underscores to camel case: num_keys -> numKeys.
-- FIXME: this is pointless overhead: remove once nothing relies on it.
key = underscore_to_camel_case(key)
key = alias_of[key] or key
local func = rawget(self, key)
if func ~= nil then
return func
elseif (m_table_new_array_funcs or get_m_table_new_array_funcs())[key] then
func = get_module_function(key, m_table or get_m_table(), "table")
elseif (m_function_new_array_funcs or get_m_function_new_array_funcs())[key] then
func = get_module_function(key, m_function or get_m_function(), "fun")
else
return nil
end
func = wrap_in_array_constructor(func)
rawset(export, key, func)
return func
end
return setmetatable(export, export_mt)