Editing Module:Array

Jump to navigation Jump to search
Warning: You are not logged in. Once you make an edit, a temporary account will be created for you. Learn more. Log in or create an account to continue receiving notifications after this account expires, and to access other features.
The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.
Latest revision Your text
Line 1: Line 1:
-- <nowiki> awawa
local libraryUtil = require('libraryUtil')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
local checkTypeMulti = libraryUtil.checkTypeMulti


---Returns the length of the array but it also works on proxy arrays
function p.any(enum, fn, clone)
---@param arr any[]
libraryUtil.checkType('Module:Enum.any', 1, enum, 'table')
---@return integer
libraryUtil.checkType('Module:Enum.any', 2, fn, 'function', true)
local function len(arr)
libraryUtil.checkType('Module:Enum.any', 3, clone, 'boolean', true)
local l = #arr
if clone then enum = mw.clone(enum) end
if l == 0 then
fn = fn or function(item) return item end
if arr[1] ~= nil then
for _, item in ipairs(enum) do
-- Exponential search to find length of proxy table
if fn(item) then
local low = 1
return true
local high = 1
local ceil = math.ceil
while arr[high] ~= nil do
high = high * 2
end
while low ~= high do
local m = ceil((low + high) / 2)
if arr[m] == nil then
high = m - 1
else
low = m
end
end
return low
else
return 0
end
end
else
return l
end
end
return false
end
end


---@class Array
function p.all(enum, fn, clone)
---@operator call(any[]): Array
libraryUtil.checkType('Module:Enum.all', 1, enum, 'table')
---@operator concat(any[]): Array
libraryUtil.checkType('Module:Enum.all', 2, fn, 'function', true)
---@operator concat(number|string|function): string
libraryUtil.checkType('Module:Enum.all', 3, clone, 'boolean', true)
---@operator unm: Array
if clone then enum = mw.clone(enum) end
---@operator add(number|number[]|Array): Array
fn = fn or function(item) return item end
---@operator sub(number|number[]|Array): Array
for _, item in ipairs(enum) do
---@operator mul(number|number[]|Array): Array
if not fn(item) then
---@operator div(number|number[]|Array): Array
---@operator pow(number|number[]|Array): Array
local Array = {
pop = table.remove,
len = len
}
Array.__index = Array
 
setmetatable(Array, {
__index = table,
__call = function (_, arr)
return Array.new(arr)
end
})
 
function Array.__concat(lhs, rhs)
if type(lhs) == 'table' and type(rhs) == 'table' then
local res = {}
local l1 = len(lhs)
for i = 1, l1 do
res[i] = lhs[i]
end
for i = 1, len(rhs) do
res[l1 + i] = rhs[i]
end
return setmetatable(res, getmetatable(lhs) or getmetatable(rhs))
else
return tostring(lhs) .. tostring(rhs)
end
end
 
function Array.__unm(arr)
return Array.map(arr, function(x) return -x end)
end
 
---@param lhs number|number[]|Array
---@param rhs number|number[]|Array
---@param funName string
---@param opName string
---@param fun fun(lhs: number, rhs: number): number
---@return Array
local function mathTemplate(lhs, rhs, funName, opName, fun)
checkTypeMulti('Module:Array.' .. funName, 1, lhs, {'number', 'table'})
checkTypeMulti('Module:Array.' .. funName, 2, rhs, {'number', 'table'})
local res = {}
 
if type(lhs) == 'number' then
for i = 1, len(rhs) do
res[i] = fun(lhs, rhs[i])
end
elseif type(rhs) == 'number' then
for i = 1, len(lhs) do
res[i] = fun(lhs[i], rhs)
end
else
assert(len(lhs) == len(rhs), string.format('Elementwise %s failed because arrays have different sizes (left: %d, right: %d)', opName, len(lhs), len(rhs)))
for i = 1, len(lhs) do
res[i] = fun(lhs[i], rhs[i])
end
end
 
return setmetatable(res, getmetatable(lhs) or getmetatable(rhs))
end
 
function Array.__add(lhs, rhs)
return mathTemplate(lhs, rhs, '__add', 'addition', function(x, y) return x + y end)
end
 
function Array.__sub(lhs, rhs)
return mathTemplate(lhs, rhs, '__sub', 'substraction', function(x, y) return x - y end)
end
 
function Array.__mul(lhs, rhs)
return mathTemplate(lhs, rhs, '__mul', 'multiplication', function(x, y) return x * y end)
end
 
function Array.__div(lhs, rhs)
return mathTemplate(lhs, rhs, '__div', 'division', function(x, y) return x / y end)
end
 
function Array.__pow(lhs, rhs)
return mathTemplate(lhs, rhs, '__pow', 'exponentiation', function(x, y) return x ^ y end)
end
 
function Array.__eq(lhs, rhs)
if len(lhs) ~= len(rhs) then
return false
end
for i = 1, len(lhs) do
if lhs[i] ~= rhs[i] then
return false
return false
end
end
Line 138: Line 31:
end
end


---Behaviour depends on the value of `fn`:
function p.each(enum, fn, clone)
---* `nil` - Checks that the array doesn't contain any **false** elements.
libraryUtil.checkType('Module:Enum.each', 1, enum, 'table')
---* `fun(elem: any, i?: integer): boolean` - Returns **true** if `fn` returns **true** for every element.
libraryUtil.checkType('Module:Enum.each', 2, fn, 'function')
---* `number` | `table` | `boolean` - Checks that all elements in `arr` are equal to this value.
libraryUtil.checkType('Module:Enum.each', 3, clone, 'boolean', true)
---@param arr any[]
if clone then enum = mw.clone(enum) end
---@param fn? any
for _, item in ipairs(enum) do
---@return boolean
fn(item)
function Array.all(arr, fn)
checkType('Module:Array.all', 1, arr, 'table')
if fn == nil then fn = function(item) return item end end
if type(fn) ~= 'function' then
local val = fn
fn = function(item) return item == val end
end
end
for i = 1, len(arr) do
---@diagnostic disable-next-line: redundant-parameter
if not fn(arr[i], i) then
return false
end
end
return true
end
end


---Behaviour depends on the value of `fn`:
function p.filter(enum, fn, clone)
---* `nil` - Checks that the array contains at least one non **false** element.
libraryUtil.checkType('Module:Enum.filter', 1, enum, 'table')
---* `fun(elem: any, i?: integer): boolean` - Returns **true** if `fn` returns **true** for at least one element.
libraryUtil.checkType('Module:Enum.filter', 2, fn, 'function', true)
---* `number` | `table` | `boolean` - Checks that `arr` contains this value.
libraryUtil.checkType('Module:Enum.filter', 3, clone, 'boolean', true)
---@param arr any[]
if clone then enum = mw.clone(enum) end
---@param fn? any
fn = fn or function(item) return item end
---@return boolean
function Array.any(arr, fn)
checkType('Module:Array.any', 1, arr, 'table')
if fn == nil then fn = function(item) return item end end
if type(fn) ~= 'function' then
local val = fn
fn = function(item) return item == val end
end
for i = 1, len(arr) do
---@diagnostic disable-next-line: redundant-parameter
if fn(arr[i], i) then
return true
end
end
return false
end
 
---Recursively removes all metatables.
---@param arr any[]
---@return any[]
function Array.clean(arr)
checkType('Module:Array.clean', 1, arr, 'table')
for i = 1, len(arr) do
if type(arr[i]) == 'table' then
Array.clean(arr[i])
end
end
setmetatable(arr, nil)
return arr
end
 
---Make a copy of the input table. Preserves metatables.
---@generic T: any[]
---@param arr T
---@param deep? boolean # Recursively clone subtables if **true**.
---@return T
function Array.clone(arr, deep)
checkType('Module:Array.clone', 1, arr, 'table')
checkType('Module:Array.clone', 2, deep, 'boolean', true)
local res = {}
for i = 1, len(arr) do
if deep == true and type(arr[i]) == 'table' then
res[i] = Array.clone(arr[i], true)
else
res[i] = arr[i]
end
end
return setmetatable(res, getmetatable(arr))
end
 
---Check if `arr` contains `val`.
---@param arr any[]
---@param val any
---@return boolean
function Array.contains(arr, val)
checkType('Module:Array.contains', 1, arr, 'table')
for i = 1, len(arr) do
if arr[i] == val then
return true
end
end
return false
end
 
---Check if `arr` contains any of the values in the table `t`.
---@param arr any[]
---@param t any[]
---@return boolean
function Array.containsAny(arr, t)
checkType('Module:Array.containsAny', 1, arr, 'table')
checkType('Module:Array.containsAny', 2, t, 'table')
local lookupTbl = {}
for i = 1, len(t) do
lookupTbl[t[i]] = true
end
for i = 1, len(arr) do
if lookupTbl[arr[i]] then
return true
end
end
return false
end
 
---Check if `arr` contains all values in the table `t`.
---@param arr any[]
---@param t any[]
---@return boolean
function Array.containsAll(arr, t)
checkType('Module:Array.containsAll', 1, arr, 'table')
checkType('Module:Array.containsAll', 2, t, 'table')
local lookupTbl = {}
local trueCount = 0
local l = len(t)
for i = 1, l do
lookupTbl[t[i]] = false
end
for i = 1, len(arr) do
if lookupTbl[arr[i]] == false then
lookupTbl[arr[i]] = true
trueCount = trueCount + 1
end
if trueCount == l then
return true
end
end
return false
end
 
---Convolute two number arrays.
---@generic T: number[]
---@param x T
---@param y T
---@return T
function Array.convolve(x, y)
checkType('Module:Array.convolve', 1, x, 'table')
checkType('Module:Array.convolve', 2, y, 'table')
local z = {}
    local xLen, yLen = len(x), len(y)
    for j = 1, (xLen + yLen - 1) do
        local sum = 0
        for k = math.max(1, j - yLen + 1), math.min(xLen, j) do
            sum = sum + x[k] * y[j-k+1]
        end
        z[j] = sum
    end
    return setmetatable(z, getmetatable(x) or getmetatable(y))
end
 
---Remove **nil** values from `arr` while preserving order.
---@generic T: any[]
---@param arr T
---@return T
function Array.condenseSparse(arr)
checkType('Module:Array.condenseSparse', 1, arr, 'table')
local keys = {}
local res = {}
local l = 0
for k in pairs(arr) do
l = l + 1
keys[l] = k
end
table.sort(keys)
for i =  1, l do
res[i] = arr[keys[i]]
end
return setmetatable(res, getmetatable(arr))
end
 
---Behaviour depends on value of `val`:
---* `nil` - Counts the number of non **false** elements.
---* `fun(elem: any): boolean` - Count the number of times the function returned **true**.
---* `boolean` | `number` | `table` - Counts the number of times this value occurs in `arr`.
---@param arr any[]
---@param val? any
---@return integer
function Array.count(arr, val)
checkType('Module:Array.count', 1, arr, 'table')
if val == nil then val = function(item) return item end end
if type(val) ~= 'function' then
local _val = val
val = function(item) return item == _val end
end
local count = 0
for i = 1, len(arr) do
if val(arr[i]) then
count = count + 1
end
end
return count
end
 
---Differentiate the array
---@generic T: number[]
---@param arr T
---@param order number? # Oder of the differentiation. Default is 1.
---@return T # Length is `#arr - order`
function Array.diff(arr, order)
checkType('Module:Array.diff', 1, arr, 'table')
checkType('Module:Array.diff', 2, order, 'number', true)
local res = {}
for i = 1, len(arr) - 1 do
res[i] = arr[i+1] - arr[i]
end
if order and order > 1 then
return Array.diff(res, order - 1)
end
return setmetatable(res, getmetatable(arr))
end
 
---Loops over `arr` and passes each element as the first argument to `fn`. This function returns nothing.
---@param arr any[]
---@param fn fun(elem: any, i?: integer)
function Array.each(arr, fn)
checkType('Module:Array.each', 1, arr, 'table')
checkType('Module:Array.each', 2, fn, 'function')
for i = 1, len(arr) do
fn(arr[i], i)
end
end
 
---Makes a copy of `arr` with only elements for which `fn` returned **true**.
---@generic T: any[]
---@param arr T
---@param fn fun(elem: any, i?: integer): boolean
---@return T
function Array.filter(arr, fn)
checkType('Module:Array.filter', 1, arr, 'table')
checkType('Module:Array.filter', 2, fn, 'function')
local r = {}
local r = {}
local l = 0
for _, item in ipairs(enum) do
for i = 1, len(arr) do
if fn(item) then
if fn(arr[i], i) then
table.insert(r, item)
l = l + 1
r[l] = arr[i]
end
end
end
end
return setmetatable(r, getmetatable(arr))
return r
end
end


---Find the first elements for which `fn` returns **true**.
function p.find(enum, fn, default, clone)
---@param arr any[]
libraryUtil.checkType('Module:Enum.find', 1, enum, 'table')
---@param fn any # A value to look for or a function of the form `fun(elem: any, i?: integer): boolean`.
libraryUtil.checkType('Module:Enum.find', 2, fn, 'function')
---@param default? any # Value to return if no element passes the test.
libraryUtil.checkType('Module:Enum.find', 4, clone, 'boolean', true)
---@return any? elem # The first element that passed the test.
if clone then enum = mw.clone(enum); default = mw.clone(default) end
---@return integer? i # The index of the item that passed the test.
for _, item in ipairs(enum) do
function Array.find(arr, fn, default)
if fn(item) then
checkType('Module:Array.find', 1, arr, 'table')
return item
checkTypeMulti('Module:Array.find_index', 2, fn, {'function', 'table', 'number', 'boolean'})
if type(fn) ~= 'function' then
local _val = fn
fn = function(item) return item == _val end
end
for i = 1, len(arr) do
---@diagnostic disable-next-line: redundant-parameter
if fn(arr[i], i) then
return arr[i], i
end
end
end
end
return default, nil
return default
end
end


---Find the index of `val`.
function p.find_index(enum, fn, default, clone)
---@param arr any[]
libraryUtil.checkType('Module:Enum.find_index', 1, enum, 'table')
---@param val any # A value to look for or a function of the form `fun(elem: any, i?: integer): boolean`.
libraryUtil.checkType('Module:Enum.find_index', 2, fn, 'function')
---@param default? any # Value to return if no element passes the test.
libraryUtil.checkType('Module:Enum.find_index', 4, clone, 'boolean', true)
---@return integer?
if clone then enum = mw.clone(enum); default = mw.clone(default) end
function Array.find_index(arr, val, default)
for index, item in ipairs(enum) do
checkType('Module:Array.find_index', 1, arr, 'table')
if fn(item) then
checkTypeMulti('Module:Array.find_index', 2, val, {'function', 'table', 'number', 'boolean'})
return index
if type(val) ~= 'function' then
local _val = val
val = function(item) return item == _val end
end
for i = 1, len(arr) do
---@diagnostic disable-next-line: redundant-parameter
if val(arr[i], i) then
return i
end
end
end
end
Line 429: Line 82:
end
end


---Extracts a subset of `arr`.
function p.map(enum, fn, clone)
---@generic T: any[]
libraryUtil.checkType('Module:Enum.map', 1, enum, 'table')
---@param arr T
libraryUtil.checkType('Module:Enum.map', 2, fn, 'function')
---@param indexes integer|integer[] # Indexes of the elements.
libraryUtil.checkType('Module:Enum.map', 3, clone, 'boolean', true)
---@return T
if clone then enum = mw.clone(enum) end
function Array.get(arr, indexes)
local r = {}
checkType('Module:Array.set', 1, arr, 'table')
for _, item in ipairs(enum) do
checkTypeMulti('Module:Array.set', 2, indexes, {'table', 'number'})
local temp = fn(item) -- Only use first returned item
if type(indexes) == 'number' then
table.insert(r, temp)
indexes = {indexes}
end
local res = {}
for i = 1, len(indexes) do
res[i] = arr[indexes[i]]
end
end
return setmetatable(res, getmetatable(arr))
return r
end
end


---Integrates the array. Effectively does $\left\{\sum^{n}_{start}{arr[n]} \,\Bigg|\, n \in [start, stop]\right\}$.
function p.max_by(enum, fn, clone)
---@generic T: number[]
libraryUtil.checkType('Module:Enum.max_by', 1, enum, 'table')
---@param arr T # number[]
libraryUtil.checkType('Module:Enum.max_by', 2, fn, 'function')
---@param start? integer # Index where to start the summation. Defaults to 1.
libraryUtil.checkType('Module:Enum.max_by', 3, clone, 'boolean', true)
---@param stop? integer # Index where to stop the summation. Defaults to #arr.
if clone then enum = mw.clone(enum) end
---@return T
return unpack(p.reduce(enum, function(new, old)
function Array.int(arr, start, stop)
local y = fn(new)
checkType('Module:Array.int', 1, arr, 'table')
return y > old[2] and {new, y} or old
checkType('Module:Array.int', 2, start, 'number', true)
end, {0, 0}))
checkType('Module:Array.int', 3, stop, 'number', true)
local res = {}
start = start or 1
stop = stop or len(arr)
res[1] = arr[start]
for i = 1, stop - start do
res[i+1] = res[i] + arr[start + i]
end
return setmetatable(res, getmetatable(arr))
end
end


---Returns an array with elements that are present in both tables.
function p.reduce(enum, fn, accumulator, clone)
---@generic T: any[]
libraryUtil.checkType('Module:Enum.reduce', 1, enum, 'table')
---@param arr1 T
libraryUtil.checkType('Module:Enum.reduce', 2, fn, 'function')
---@param arr2 T
libraryUtil.checkType('Module:Enum.reduce', 4, clone, 'boolean', true)
---@return T
if clone then enum = mw.clone(enum); accumulator = mw.clone(accumulator) end
function Array.intersect(arr1, arr2)
local acc = accumulator
checkType('Module:Array.intersect', 1, arr1, 'table')
for index, item in ipairs(enum) do
checkType('Module:Array.intersect', 2, arr2, 'table')
if index == 1 and not accumulator then
local arr2Elements = {}
acc = item
local res = {}
else
local l = 0
acc = fn(item, acc)
Array.each(arr2, function(item) arr2Elements[item] = true end)
Array.each(arr1, function(item)
if arr2Elements[item] then
l = l + 1
res[l] = item
end
end
end)
return setmetatable(res, getmetatable(arr1) or getmetatable(arr2))
end
---Checks if the two inputs have at least one element in common.
---@param arr1 any[]
---@param arr2 any[]
---@return boolean
function Array.intersects(arr1, arr2)
checkType('Module:Array.intersects', 1, arr1, 'table')
checkType('Module:Array.intersects', 2, arr2, 'table')
local small = {}
local large
if len(arr1) <= len(arr2) then
Array.each(arr1, function(item) small[item] = true end)
large = arr2
else
Array.each(arr2, function(item) small[item] = true end)
large = arr1
end
end
return Array.any(large, function(item) return small[item] end)
return acc
end
end


---Inserts values into `arr`.
function p.reject(enum, fn, clone)
---@generic T: any[]
libraryUtil.checkType('Module:Enum.reject', 1, enum, 'table')
---@param arr T
libraryUtil.checkType('Module:Enum.reject', 2, fn, 'function')
---@param val any # If `val` is an array and `unpackVal` is **true** then the individual elements of `val` are inserted.
libraryUtil.checkType('Module:Enum.reject', 3, clone, 'boolean', true)
---@param index? integer # Location to start the insertion. Default is at the end of `arr`.
if clone then enum = mw.clone(enum) end
---@param unpackVal? boolean # Default is **false**.
fn = fn or function(item) return item end
---@return T
---@overload fun(arr: T, val: any, unpackVal: boolean): T
function Array.insert(arr, val, index, unpackVal)
checkType('Module:Array.insert', 1, arr, 'table')
checkTypeMulti('Module:Array.insert', 3, index, {'number', 'boolean', 'nil'})
checkType('Module:Array.insert', 4, unpackVal, 'boolean', true)
if type(index) == 'boolean'  then
unpackVal, index = index, nil
end
local l = len(arr)
index = index or (l + 1)
local mt = getmetatable(arr)
setmetatable(arr, nil)
 
if unpackVal and type(val) == 'table' then
local l2 = len(val)
for i = 0, l - index do
arr[l + l2 - i] = arr[l - i]
end
for i = 0, l2 - 1 do
arr[index + i] = val[i + 1]
end
else
table.insert(arr, index, val)
end
 
return setmetatable(arr, mt)
end
 
---Returns the last element of `arr`.
---@param arr any[]
---@param offset? integer
---@return any
function Array.last(arr, offset)
checkType('Module:Array.last', 1, arr, 'table')
checkType('Module:Array.last', 2, offset, 'number', true)
return arr[len(arr) + offset]
end
 
---Returns a new table were each element of `arr` is modified by `fn`.
---@generic T: any[]
---@param arr T
---@param fn fun(elem: any, i?: integer): any # First argument is the current element, the second argument is the index of the current element.
---@return T
function Array.map(arr, fn)
checkType('Module:Array.map', 1, arr, 'table')
checkType('Module:Array.map', 2, fn, 'function')
local l = 0
local r = {}
local r = {}
for i = 1, len(arr) do
for _, item in ipairs(enum) do
local tmp = fn(arr[i], i)
if not fn(item) then
if tmp ~= nil then
table.insert(r, item)
l = l + 1
r[l] = tmp
end
end
end
end
return setmetatable(r, getmetatable(arr))
return r
end
 
---Find the element for which `fn` returned the largest value.
---@param arr any[]
---@param fn fun(elem: any): any # The returned value needs to be comparable using the `<` operator.
---@return any elem # The element with the largest `fn` value.
---@return integer i # The index of this element.
function Array.max_by(arr, fn)
checkType('Module:Array.max_by', 1, arr, 'table')
checkType('Module:Array.max_by', 2, fn, 'function')
return unpack(Array.reduce(arr, function(new, old, i)
local y = fn(new)
return y > old[2] and {new, y, i} or old
end, {nil, -math.huge}))
end
 
---Find the largest value in the array.
---@param arr any[] # The values need to be comparable using the `<` operator.
---@return any elem
---@return integer i # The index of the largest value.
function Array.max(arr)
checkType('Module:Array.max', 1, arr, 'table')
local val, _, i = Array.max_by(arr, function(x) return x end)
return val, i
end
 
---Find the smallest value in the array.
---@param arr any[] # The values need to be comparable using the `<` operator.
---@return any elem
---@return integer i # The index of the smallest value.
function Array.min(arr)
checkType('Module:Array.min', 1, arr, 'table')
local val, _, i = Array.max_by(arr, function(x) return -x end)
return val, i
end
end


---Turn the input table into an Array. This makes it possible to use the colon `:` operator to access the Array methods.
function p.range(start, stop, step)
---
libraryUtil.checkType('Module:Enum.range', 1, start, 'number')
---It also enables the use of math operators with the array.
libraryUtil.checkType('Module:Enum.range', 2, stop, 'number', true)
---```
libraryUtil.checkType('Module:Enum.range', 3, step, 'number', true)
---local x = arr.new{ 1, 2, 3 }
local array = {}
---local y = arr{ 4, 5, 6 } -- Alternative notation
---
---print( -x ) --> { -1, -2, -3 }
---print( x + 2 ) --> { 3, 4, 5 }
---print( x - 2 ) --> { -1, 0, 1 }
---print( x * 2 ) --> { 2, 4, 6 }
---print( x / 2 ) --> { 0.5, 1, 1.5 }
---print( x ^ 2 ) --> { 1, 4, 9 }
---
---print( x + y ) --> { 5, 7, 9 }
---print( x .. y ) --> { 1, 2, 3, 4, 5, 6 }
---print( (x .. y):reject{3, 4, 5} ) --> { 1, 2, 6 }
---print( x:sum() ) --> 6
---
---print( x:update( {1, 3}, y:get{2, 3} * 2 ) ) --> { 10, 2, 12 }
---```
---@param arr? any[]
---@return Array
function Array.new(arr)
local obj = arr or {}
for _, v in pairs(obj) do
if type(v) == 'table' then
Array.new(v)
end
end
 
if getmetatable(obj) == nil then
setmetatable(obj, Array)
end
 
return obj
end
 
---Creates an object that returns a value that is `step` higher than the previous value each time it gets called.
---
---The stored value can be read without incrementing by reading the `val` field.
---
---A new stored value can be set through the `val` field.
---
---A new step size can be set through the `step` field.
---```
---local inc = arr.newIncrementor(10, 5)
---print( inc() ) --> 10
---print( inc() ) --> 15
---print( inc.val ) --> 15
---inc.val = 100
---inc.step = 20
---print( inc.val ) --> 100
---print( inc() ) --> 120
---```
---@param start? number # Default is 1.
---@param step? number # Default is 1.
---@return Incrementor
function Array.newIncrementor(start, step)
checkType('Module:Array.newIncrementor', 1, start, 'number', true)
checkType('Module:Array.newIncrementor', 2, step, 'number', true)
step = step or 1
local n = (start or 1) - step
---@class Incrementor
local obj = {}
return setmetatable(obj, {
__call = function() n = n + step return n end,
__tostring = function() return tostring(n) end,
__index = function() return n end,
__newindex = function(self, k, v)
if k == 'step' and type(v) == 'number' then
step = v
elseif type(v) == 'number' then
n = v
end
end,
__concat = function(x, y) return tostring(x) .. tostring(y) end
})
end
 
---Returns a range of numbers.
---@param start number # Start value inclusive.
---@param stop number # Stop value inclusive for integers, exclusive for floats.
---@param step? number # Default is 1.
---@return Array
---@overload fun(stop: number): Array
function Array.range(start, stop, step)
checkType('Module:Array.range', 1, start, 'number')
checkType('Module:Array.range', 2, stop, 'number', true)
checkType('Module:Array.range', 3, step, 'number', true)
local arr = {}
local len = 0
if not stop then
if not stop then
stop = start
stop = start
Line 703: Line 147:
end
end
for i = start, stop, step or 1 do
for i = start, stop, step or 1 do
len = len + 1
table.insert(array, i)
arr[len] = i
end
end
return setmetatable(arr, Array)
return array
end
end


---Condenses the array into a single value.
function p.scan(enum, fn, accumulator, clone)
---
libraryUtil.checkType('Module:Enum.scan', 1, enum, 'table')
---For each element `fn` is called with the current element, the current accumulator, and the current element index. The returned value of `fn` becomes the accumulator for the next element.
libraryUtil.checkType('Module:Enum.scan', 2, fn, 'function')
---
libraryUtil.checkType('Module:Enum.scan', 4, clone, 'boolean', true)
---If no `accumulator` value is given at the start then the first element off `arr` becomes the accumulator and the iteration starts from the second element.
if clone then enum = mw.clone(enum); accumulator = mw.clone(accumulator) end
---```
---local t = { 1, 2, 3, 4 }
---local sum = arr.reduce( t, function(elem, acc) return acc + elem end ) -- sum == 10
---```
---@param arr any[]
---@param fn fun(elem: any, acc: any, i?: integer): any # The result of this function becomes the `acc` for the next element.
---@param accumulator? any
---@return any # This is the last accumulator value.
function Array.reduce(arr, fn, accumulator)
checkType('Module:Array.reduce', 1, arr, 'table')
checkType('Module:Array.reduce', 2, fn, 'function')
local acc = accumulator
local acc = accumulator
local start = 1
if acc == nil then
acc = arr[1]
start = 2
end
for i = start, len(arr) do
acc = fn(arr[i], acc, i)
end
return acc
end
---Make a copy off `arr` with certain values removed.
---
---Behaviour for different values of `val`:
---* `boolean` | `number` - Remove values equal to this.
---* `table` - Remove all values in this table.
---* `fun(elem: any, i?: integer): boolean` - Remove elements for which the functions returns **true**.
---@generic T: any[]
---@param arr T
---@param val table|function|number|boolean
---@return T
function Array.reject(arr, val)
checkType('Module:Array.reject', 1, arr, 'table')
checkTypeMulti('Module:Array.reject', 2, val, {'function', 'table', 'number', 'boolean'})
if type(val) ~= 'function' and type(val) ~= 'table' then
val = {val}
end
local r = {}
local r = {}
local l = 0
for index, item in ipairs(enum) do
if type(val) == 'function' then
if index == 1 and not accumulator then
for i = 1, len(arr) do
acc = item
if not val(arr[i], i) then
else
l = l + 1
acc = fn(item, acc)
r[l] = arr[i]
end
end
else
local rejectMap = {}
Array.each(val --[[@as any[] ]], function(item) rejectMap[item] = true end)
for i = 1, len(arr) do
if not rejectMap[arr[i]] then
l = l + 1
r[l] = arr[i]
end
end
end
table.insert(r, acc)
end
end
return setmetatable(r, getmetatable(arr))
return r
end
end


---Returns an Array with `val` repeated `n` times.
function p.slice(enum, start, finish, clone)
---@param val any
libraryUtil.checkType('Module:Enum.slice', 1, enum, 'table')
---@param n integer
libraryUtil.checkType('Module:Enum.slice', 2, start, 'number', true)
---@return Array
libraryUtil.checkType('Module:Enum.slice', 3, finish, 'number', true)
function Array.rep(val, n)
libraryUtil.checkType('Module:Enum.slice', 4, clone, 'boolean', true)
checkType('Module:Array.rep', 2, n, 'number')
if clone then enum = mw.clone(enum) end
local r = {}
start = start or 1
for i = 1, n do
finish = finish or #enum
r[i] = val
end
return setmetatable(r, Array)
end
 
---Condenses the array into a single value while saving every accumulator value.
---
---For each element `fn` is called with the current element, the current accumulator, and the current element index. The returned value of `fn` becomes the accumulator for the next element.
---
---If no `accumulator` value is given at the start then the first element off `arr` becomes the accumulator and the iteration starts from the second element.
---```
---local t = { 1, 2, 3, 4 }
---local x = arr.scan( t, function(elem, acc) return acc + elem end ) -- x = { 1, 3, 6, 10 }
---```
---@generic T: any[]
---@param arr T
---@param fn fun(elem: any, acc: any, i?: integer): any # Returned value becomes the accumulator for the next element.
---@param accumulator? any
---@return T
function Array.scan(arr, fn, accumulator)
checkType('Module:Array.scan', 1, arr, 'table')
checkType('Module:Array.scan', 2, fn, 'function')
local acc = accumulator
local r = {}
local r = {}
for i = 1, len(arr) do
for index, item in ipairs(enum) do
if i == 1 and not accumulator then
if index >= start and index <= finish then
acc = arr[i]
table.insert(r, item)
else
acc = fn(arr[i], acc, i)
end
end
r[i] = acc
end
end
return setmetatable(r, getmetatable(arr))
return r
end
end


---Update a range of index with a range of values.
function p.split(enum, count, clone)
---
libraryUtil.checkType('Module:Enum.split', 1, enum, 'table')
---If if only one value is given but multiple indexes than that value is set for all those indexes.
libraryUtil.checkType('Module:Enum.split', 2, count, 'number')
---
libraryUtil.checkType('Module:Enum.split', 3, clone, 'boolean', true)
---If `values` is a table then it must of the same length as `indexes`.
if clone then enum = mw.clone(enum) end
---@generic T: any[]
if #enum < count then
---@param arr T
return enum, {}
---@param indexes integer|integer[]
elseif count < 1 then
---@param values any|any[]
return {}, enum
---@return T
function Array.set(arr, indexes, values)
checkType('Module:Array.set', 1, arr, 'table')
checkTypeMulti('Module:Array.set', 2, indexes, {'table', 'number'})
local mt = getmetatable(arr)
setmetatable(arr, nil)
if type(indexes) == 'number' then
indexes = {indexes}
end
end
if type(values) == 'table' then
assert(len(indexes) == len(values), string.format("Module:Array.set: 'indexes' and 'values' arrays are not equal length (#indexes = %d, #values = %d)", len(indexes), len(values)))
for i = 1, len(indexes) do
arr[indexes[i]] = values[i]
end
else
for i = 1, len(indexes) do
arr[indexes[i]] = values
end
end
return setmetatable(arr, mt)
end


---Extract a subtable from `arr`.
---@generic T: any[]
---@param arr T
---@param start integer # Start index. Use negative values to count form the end of the array.
---@param stop integer # Stop index. Use negative values to count form the end of the array.
---@return T
---@overload fun(arr: T, stop: integer): T
function Array.slice(arr, start, stop)
checkType('Module:Array.slice', 1, arr, 'table')
checkType('Module:Array.slice', 2, start, 'number', true)
checkType('Module:Array.slice', 3, stop, 'number', true)
start = start or len(arr)
if start < 0 then
start = len(arr) + start
end
if stop == nil then
stop = start
start = 1
end
if stop < 0 then
stop = len(arr) + stop
end
local r = {}
local len = 0
for i = start, stop do
len = len + 1
r[len] = arr[i]
end
return setmetatable(r, getmetatable(arr))
end
---Split `arr` into two arrays.
---@generic T: any[]
---@param arr T
---@param index integer # Index to split on.
---@return T x # [1, index]
---@return T y # [index + 1, #arr]
function Array.split(arr, index)
checkType('Module:Array.split', 1, arr, 'table')
checkType('Module:Array.split', 2, index, 'number')
local x = {}
local x = {}
local y = {}
local y = {}
for i = 1, len(arr) do
 
table.insert(i <= index and x or y, arr[i])
for i = 1, #enum do
table.insert(
i <= count and x or y,
enum[i]
)
end
end
return setmetatable(x, getmetatable(arr)), setmetatable(y, getmetatable(arr))
return x, y
end
end


---Returns the sum of all elements of `arr`.
function p.sum(enum, clone)
---@param arr number[]
libraryUtil.checkType('Module:Enum.sum', 1, enum, 'table')
---@return number
libraryUtil.checkType('Module:Enum.sum', 2, clone, 'boolean', true)
function Array.sum(arr)
if clone then enum = mw.clone(enum) end
checkType('Module:Array.sum', 1, arr, 'table')
return p.reduce(enum, function(x, y) return x + y end)
local res = 0
for i = 1, len(arr) do
res = res + arr[i]
end
return res
end
end


---Extract a subtable from `arr`.
function p.take(enum, count, clone)
---@generic T: any[]
libraryUtil.checkType('Module:Enum.take', 1, enum, 'table')
---@param arr T
libraryUtil.checkType('Module:Enum.take', 2, count, 'number')
---@param count integer # Length of the subtable.
libraryUtil.checkType('Module:Enum.take', 3, clone, 'boolean', true)
---@param start? integer # Start index. Default is 1.
if clone then enum = mw.clone(enum) end
---@return T
local x, _ = p.split(enum, count)
function Array.take(arr, count, start)
return x
checkType('Module:Array.take', 1, arr, 'table')
checkType('Module:Array.take', 2, count, 'number')
checkType('Module:Array.take', 3, start, 'number', true)
local x = {}
start = start or 1
for i = start, math.min(len(arr), count + start - 1) do
table.insert(x, arr[i])
end
return setmetatable(x, getmetatable(arr))
end
end


---Extract a subtable from `arr`.
function p.take_every(enum, n, clone)
---```
libraryUtil.checkType('Module:Enum.take_every', 1, enum, 'table')
---local t = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
libraryUtil.checkType('Module:Enum.take_every', 2, n, 'number')
---local x = arr.take_every( t, 2 )      --> x = { 1, 3, 5, 7, 9 }
libraryUtil.checkType('Module:Enum.take_every', 3, clone, 'boolean', true)
---local x = arr.take_every( t, 2, 3 )    --> x = { 3, 5, 7, 9 }
if clone then enum = mw.clone(enum) end
---local x = arr.take_every( t, 2, 3, 2 ) --> x = { 3, 5 }
--- ```
---@generic T: any[]
---@param arr T
---@param n integer # Step size.
---@param start? integer # Start index.
---@param count? integer # Max amount of elements to get.
---@return T
function Array.take_every(arr, n, start, count)
checkType('Module:Array.take_every', 1, arr, 'table')
checkType('Module:Array.take_every', 2, n, 'number')
checkType('Module:Array.take_every', 3, start, 'number', true)
checkType('Module:Array.take_every', 4, count, 'number', true)
count = count or len(arr)
start = start or 1
local stop = math.min(len(arr), start + n * (count - 1))
local r = {}
local r = {}
local l = 0
for index, item in ipairs(enum) do
for i = start, stop, n do
if (index - 1) % n == 0 then
l = l + 1
table.insert(r, item)
r[l] = arr[i]
end
end
end
return setmetatable(r, getmetatable(arr))
return r
end
end


---Return a new table with all duplicates removed.
function p.unique(enum, fn, clone)
---@generic T: any[]
libraryUtil.checkType('Module:Enum.unique', 1, enum, 'table')
---@param arr T
libraryUtil.checkType('Module:Enum.unique', 2, fn, 'function', true)
---@param fn? fun(elem: any): any # Function to generate an id for each element. The result will then contain elements that generated unique ids.
libraryUtil.checkType('Module:Enum.unique', 3, clone, 'boolean', true)
---@return T
if clone then enum = mw.clone(enum) end
function Array.unique(arr, fn)
checkType('Module:Array.unique', 1, arr, 'table')
checkType('Module:Array.unique', 2, fn, 'function', true)
fn = fn or function(item) return item end
fn = fn or function(item) return item end
local r = {}
local r = {}
local l = 0
local hash = {}
local hash = {}
for i = 1, len(arr) do
for _, item in ipairs(enum) do
local id = fn(arr[i])
local id = fn(item)
if not hash[id] then
if not hash[id] then
l = l + 1
table.insert(r, item)
r[l] = arr[i]
hash[id] = true
hash[id] = true
end
end
end
end
return setmetatable(r, getmetatable(arr))
return r
end
end


---Combine elements with the same index from multiple arrays.
function p.zip(enums, clone)
---```
libraryUtil.checkType('Module:Enum.zip', 1, enums, 'table')
---local x = {1, 2, 3}
libraryUtil.checkType('Module:Enum.zip', 2, clone, 'boolean', true)
---local y = {4, 5, 6, 7}
if clone then enums = mw.clone(enums) end
---local z = arr.zip( x, y ) --> z = { { 1, 4 }, { 2, 5 }, { 3, 6 }, { 7 } }
---```
---@param ... any[]
---@return Array
function Array.zip(...)
local arrs = { ... }
checkType('Module:Array.zip', 1, arrs[1], 'table')
local r = {}
local r = {}
local _, longest = Array.max_by(arrs, function(arr) return len(arr) end)
local _, longest = p.max_by(enums, function(enum) return #enum end)
for i = 1, longest do
for i = 1, longest do
local q = {}
local q = {}
for j = 1, len(arrs) do
for j = 1, #enums do
table.insert(q, arrs[j][i])
table.insert(q, enums[j][i])
end
table.insert(r, setmetatable(q, Array))
end
return setmetatable(r, Array)
end
 
-- Range indexing has a performance impact so this is placed in a separate subclass
Array.RI_mt = {}
for k, v in pairs(Array) do
Array.RI_mt[k] = v
end
 
function Array.RI_mt.__index(t, k)
if type(k) == 'table' then
local res = {}
for i = 1, len(k) do
res[i] = t[k[i]]
end
end
return setmetatable(res, Array)
table.insert(r, q)
else
return Array[k]
end
end
return r
end
end


function Array.RI_mt.__newindex(t, k, v)
function p.take_from(enum, index, count)
if type(k) == 'table' then
local x, _ = p.split(p.reject(enum,  
if type(v) == 'table' then
function(item, idx)
for i = 1, len(k) do
return idx < index
t[k[i]] = v[i]
end
else
for i = 1, len(k) do
t[k[i]] = v
end
end
end
else
), count)
rawset(t, k, v)
return x
end
end
end


---Enable range indexing on the input array.
function p.intersect(enum1, enum2, clone)
---
libraryUtil.checkType('Module:Enum.intersect', 1, enum1, 'table')
---This has a performance impact on reads and writes to the table.
libraryUtil.checkType('Module:Enum.intersect', 2, enum2, 'table')
---```
libraryUtil.checkType('Module:Enum.intersect', 3, clone, 'boolean', true)
---local t = arr{10, 11, 12, 13, 14, 15}:ri()
if clone then enum1 = mw.clone(enum1); enum2 = mw.clone(enum2) end
---print( t[{2, 3}] ) --> { 11, 12 }
return p.any(enum1, function(item1) return p.any(enum2, function(item2) return item1==item2 end) end)
---```
---@param arr any[]
---@param recursive? boolean # Default is false.
---@return Array
function Array.ri(arr, recursive)
checkType('Module:Array.ri', 1, arr, 'table')
checkType('Module:Array.ri', 2, recursive, 'boolean', true)
arr = arr or {}
if recursive then
for _, v in pairs(arr) do
if type(v) == 'table' then
Array.ri(v, true)
end
end
end
 
if getmetatable(arr) == nil or getmetatable(arr) == Array then
setmetatable(arr, Array.RI_mt)
end
 
return arr
end
end


---Globally enable range indexing on all Array objects by default.
function p.contains(enum, elem, clone)
---@param set boolean
libraryUtil.checkType('Module:Enum.contains', 1, enum, 'table')
function Array.allwaysAllowRangeIndexing(set)
libraryUtil.checkType('Module:Enum.contains', 3, clone, 'boolean', true)
checkType('Module:Array.allwaysAllowRangeIndexing', 1, set, 'boolean')
if clone then enum = mw.clone(enum); elem = mw.clone(elem) end
if set then
return p.any(enum, function(item) return item == elem end)
Array.__index = Array.RI_mt.__index
Array.__newindex = Array.RI_mt.__newindex
else
Array.__index = Array
Array.__newindex = nil
end
end
end


return Array
return p
-- </nowiki>
Please note that all contributions to The Deadlock Wiki are considered to be released under the Creative Commons Attribution-NonCommercial-ShareAlike (see Deadlock:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!
Cancel Editing help (opens in new window)
Preview page with this template

Page included on this page: