Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Please sign up or log in to edit the wiki.

Module:Array

From the Star Citizen Wiki, the fidelity™ encyclopedia
Revision as of 17:19, 24 June 2023 by Alistair3149 (talk | contribs) (Remove logger dependency)
Module documentation[view][edit][history][purge]
This documentation is transcluded from Module:Array/doc. Changes can be proposed in the talk page.
Error: Module:DependencyList:204: bad argument #3 to 'Module:Array.insert' (number expected, got boolean)
Function list
L 15 — arr.__index
L 27 — arr.__tostring
L 34 — arr.__concat
L 50 — arr.__unm
L 54 — mathTemplate
L 77 — arr.__add
L 81 — arr.__sub
L 85 — arr.__mul
L 89 — arr.__div
L 93 — arr.__pow
L 97 — arr.__lt
L 106 — arr.__le
L 115 — arr.__eq
L 127 — arr.all
L 144 — arr.any
L 161 — arr.clean
L 172 — arr.contains
L 190 — arr.count
L 206 — arr.diff
L 219 — arr.each
L 229 — arr.filter
L 249 — arr.find
L 266 — arr.find_index
L 283 — arr.newIncrementor
L 304 — arr.int
L 318 — arr.intersect
L 334 — arr.intersects
L 349 — arr.insert
L 371 — arr.map
L 388 — arr.max_by
L 397 — arr.max
L 403 — arr.min
L 409 — arr.new
L 424 — arr.range
L 441 — arr.reduce
L 457 — arr.reject
L 490 — arr.rep
L 499 — arr.scan
L 517 — arr.slice
L 541 — arr.split
L 552 — arr.sum
L 561 — arr.take
L 574 — arr.take_every
L 589 — arr.unique
L 609 — arr.update
L 628 — arr.zip

This module is a helper module to be used by other modules; it may not designed to be invoked directly. See Star Citizen:Lua/Helper modules for a full list and more information. For a full list of modules using this helper click here

FunctionTypeUse
all( arr, [fn] )arr: any[]
fn?: any
-> boolean
Behaviour depends on the value of fn:
  • nil - Checks that the array doesn't contain any false elements.
  • fun(elem: any, i?: integer): boolean - Returns true if fn returns true for every element.
  • number | table | boolean - Checks that all elements in arr are equal to this value.
any( arr, [fn] )arr: any[]
fn?: any
-> boolean
Behaviour depends on the value of fn:
  • nil - Checks that the array contains at least one non false element.
  • fun(elem: any, i?: integer): boolean - Returns true if fn returns true for at least one element.
  • number | table | boolean - Checks that arr contains this value.
clean( arr )arr: any[]
-> any[]
Recursively removes all metatables.
clone( arr, [deep] )arr: any[]
deep?: boolean
-> any[]
Make a copy of the input table. Preserves metatables.
contains( arr, val )arr: any[]
val: any
-> boolean
Check if arr contains val.
containsAny( arr, t )arr: any[]
t: any[]
-> boolean
Check if arr contains any of the values in the table t.
containsAll( arr, t )arr: any[]
t: any[]
-> boolean
Check if arr contains all values in the table t.
convolve( x, y )x: number[]
y: number[]
-> number[]
Convolute two number arrays.
condenseSparse( arr )arr: any[]
-> any[]
Remove nil values from arr while preserving order.
count( arr, fn )arr: any[]
fn: any
-> integer
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.
diff( arr, [order|1] )arr: number[]
order?: number
-> number[]
Differentiates arr. The length of the result is #arr - order long.
each( arr, fn )arr: any[]
fn: fun(elem: any, i?: integer)
Loops over the array part of arr and passes each element as the first argument to fn. This function returns nothing.
filter( arr, fn )arr: any[]
fn: fun(elem: any, i?: integer): boolean
-> any[]
Makes a copy of arr with only elements for which fn returned true.
find( arr, fn, [default] )arr: any[]
fn: any
default?: any
-> any?, integer?
Behaviour depends on the value of fn:
  • fun(elem: any, i?: integer): boolean - Find the first elements for which fn returns true.
  • boolean | number | table - Find the first occurance of this value.
Returns two values: the element itself and its index.
find_index( arr, fn, [default] )arr: any[]
fn: any
default?: any
-> integer?
Behaviour depends on the value of fn:
  • fun(elem: any, i?: integer): boolean - Find the index of the first elements for which fn returns true.
  • boolean | number | table - Find the index of the first occurance of this value.
get( arr, indexes )arr: any[]
indexes: integer|integer[]
-> any[]
Extracts a subset of arr.
int( arr, [start|1], [stop|#arr] )arr: number[]
start?: number
stop?: number
-> number[]
Integrates arr from index start to stop. Effectively does <math>\left\{\sum^{n}_{start}{arr[n]} \,\Bigg
intersect( arr1, arr2 )arr1: any[]
arr2: any[]
-> any[]
Returns an array with elements that are present in both tables.
intersects( arr1, arr2 )arr1: any[]
arr2: any[]
-> boolean
Checks if the two inputs have at least one element in common.
insert( arr, val, [index], [unpackVal] )
OR
insert( arr, val, [unpackVal] )
arr: any[]
val: any
index?: integer
unpackVal?: boolean
-> any[]
Inserts values into arr. If val is an array and unpackVal is true then the individual elements of val are inserted. index is the location to start the insertion. Default is at the end of arr.
last( arr )arr: any[]
-> any
Returns the last element of arr.
len( arr )arr: any[]
-> integer
Returns the length of the array but it also works on proxy arrays like mw.loadData or mw.loadJsonData.
map( arr, fn )arr: any[]
fn: fun(elem: any, i?: integer): any
-> any[]
Returns a new table were each element of arr is modified by fn.
max_by( arr, fn )arr: any[]
fn: fun(elem: any): any
-> any, integer
Find the element for which fn returned the largest value. The returned value of fn needs to be comparable using the < operator. Returns three values: The element with the largest fn value, its fn result, and its index.
max( arr )arr: any[]
-> any, integer
Find the largest value in the array. The values need to be comparable using the < operator. Returns two values: the element and its index.
min( arr )arr: any[]
-> any, integer
Find the smallest value in the array. The values need to be comparable using the < operator. Returns two values: the element and its index.
new( [arr|{}] )arr?: any[]
-> any[]
Turn the input table into an Array. This makes it possible to use the colon : operator to access the Array methods. It also enables the use of math operators with the array.
local x = arr.new{ 1, 2, 3 }
local y = arr{ 4, 5, 6 } -- Alternative notation

mw.logObject( -x ) --> { -1, -2, -3 }
mw.logObject( x + 2 ) --> { 3, 4, 5 }
mw.logObject( x - 2 ) --> { -1, 0, 1 }
mw.logObject( x * 2 ) --> { 2, 4, 6 }
mw.logObject( x / 2 ) --> { 0.5, 1, 1.5 }
mw.logObject( x ^ 2 ) --> { 1, 4, 9 }

mw.logObject( x + y ) --> { 5, 7, 9 }
mw.logObject( x .. y ) --> { 1, 2, 3, 4, 5, 6 }
mw.logObject( (x .. y):reject{3, 4, 5} ) --> { 1, 2, 6 }
mw.logObject( x:sum() ) --> 6

mw.logObject( x:update( {1, 3}, y:get{2, 3} * 2 ) ) --> { 10, 2, 12 }
newIncrementor( [start|1], [step|1] )start?: number
step?: number
-> Incrementor
Returns a new incrementor function. Every time this incrementor function is called it returns a number step higher than the previous call. The current value can be obtained with inc.n or set inc.n = number where inc is an incrementor function. The step size can be changed with inc.step = number.
range( stop )
OR
range( start, stop, [step|1] )
start: number
stop: number
step?: number
-> number[]
Returns a table containing a sequence of numbers from start to stop (both inclusive if ints, end-exclusive if floats) by step. range(4) produces {1, 2, 3, 4} (start defaults to 1). range(0, 4) produces {0, 1, 2, 3, 4}. When step is given, it specifies the increment (or decrement).
reduce( arr, fn, [accumulator|arr[1]] )arr: any[]
fn: fun(elem: any, acc: any, i?: integer): any
accumulator?: any
-> any
Condenses the array into a single 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 sum = arr.reduce( t, function(elem, acc) return acc + elem end ) -- sum == 10
reject( arr, val )arr: any[]
val: any
-> any[]
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.
rep( val, n )val: any
n: number
-> any[]
Returns a table with n copies of val.
scan( arr, fn, [accumulator|arr[1]] )arr: any[]
fn: fun(elem: any, acc: any, i?: integer): any
accumulator?: any
-> any[]
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 }
set( arr, indexes, values )arr: any[]
indexes: integer|integer[]
values: any|any[]
-> any[]
Update a range of index with a range of values.

If if only one value is given but multiple indexes than that value is set for all those indexes.

If values is a table then it must of the same length as indexes.
slice( arr, [start|1], [stop|#arr] )
OR
slice( arr, stop )
arr: any[]
start?: number
stop?: number
-> any[]
Returns a table containing all the elements of arr between the start and stop indices. The start and stop indices are inclusive. If start or stop are negative values then they are referenced to the end of the table.
split( arr, index )arr: any[]
index: number
-> any[], any[]
Split arr into two arrays. Retuns two tables. The first contains elements from [1, index], and the second from [index + 1, #arr].
sum( arr )arr: number[]
-> number
Returns the sum of all elements of arr.
take( arr, count, [start|1] )arr: any[]
count: number
start?: number
-> any[]
Extract a subtable from arr of count elements long starting from the start index.
take_every( arr, n, [start|1], [count|#arr] )arr: any[]
n: integer
start?: integer
count?: integer
-> any[]
Extract a subtable from arr.
local t = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
local x = arr.take_every( t, 2 )       --> x = { 1, 3, 5, 7, 9 }
local x = arr.take_every( t, 2, 3 )    --> x = { 3, 5, 7, 9 }
local x = arr.take_every( t, 2, 3, 2 ) --> x = { 3, 5 }
unique( arr, [fn] )arr: any[]
fn?: fun(elem: any): any
-> any[]
Return a new table with all duplicates removed. fn is an optional function to generate an id for each element. The result will then contain elements that generated unique ids. The order of first occurance is preserved.
zip( ... )...any[]
-> any[][]
Combine elements with the same index from multiple arrays.
local x = {1, 2, 3}
local y = {4, 5, 6, 7}
local z = arr.zip( x, y ) --> z = { { 1, 4 }, { 2, 5 }, { 3, 6 }, { 7 } }
Example:
local arr = require( 'Module:Array' )

local x = arr{1, 2, 3, 4, 10}
local y = arr{'a', 'b', 'b', 1}

arr.any( x, function( item ) return item == 3 end ) --> true
arr.all( y, function( item ) return type( item ) == 'string' end ) --> false
arr.map( x, function( item ) return item * 2 end ) --> { 2, 4, 6, 8, 20 }
arr.filter( y, function( item ) return type( item ) == 'string' end ) --> { "a", "b", "b" }
arr.reject( y, function( item ) return type( item ) == 'string' end ) --> { 1 }
arr.find( x, function( item ) return item > 5 end ) --> 10,  5
arr.find_index( y, function( item ) return type( item ) ~= 'string' end ) --> 4
arr.max_by( x, function( item ) return item * 2 end ) --> 10, 20, 5
arr.reduce( x, function( item, acc ) return acc + item*item end, 5 ) --> 135
arr.range( 10, 1, -3 ) --> { 10, 7, 4, 1 }
arr.scan( x, function( item, acc ) return acc + item*item end, 5 ) --> { 6, 10, 19, 35, 135 }
arr.slice( x, 2, 4 ) --> { 2, 3, 4 }
arr.split( x, 2 ) --> { 1, 2 }, { 3, 4, 10 }
arr.sum( x ) --> 20
arr.take( x, 2 ) --> { 1, 2 }
arr.take_every( x, 2 ) --> { 1, 3, 10 }
arr.unique( y ) --> { "a", "b", 1 }
arr.zip( x, y, {20, 30} ) --> { { 1, "a", 20 }, { 2, "b", 30 }, { 3, "b" }, { 4, 1 }, { 10 } }
arr.intersect( x, y ) --> { 1 }
arr.intersects( x, y ) --> true
arr.contains({ 1, 2, 3}, 3) --> true
arr.diff( x ) --> { 1, 1, 1, 6 }
arr.int( x ) --> { 1, 3, 6, 10, 20 }
arr.insert( x, y, 3 ) --> { 1, 2, { "a", "b", "b", 1 }, 3, 4, 10 }

inc = arr.newIncrementor( 10, 5 )
print( inc() ) --> 10
print( inc() ) --> 15

-- Imported from: https://runescape.wiki/w/Module:Array

-- <nowiki>
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeMulti = libraryUtil.checkTypeMulti
local arr = {}

setmetatable(arr, {
    __call = function (_, array)
        return arr.new(array)
    end
})

function arr.__index(t, k)
    if type(k) == 'table' then
        local res = arr.new()
        for i = 1, #t do
            res[i] = t[k[i]]
        end
        return res
    else
        return arr[k]
    end
end

function arr.__tostring(array)
    setmetatable(array, nil)
    local str = mw.logObject( array )
    setmetatable(array, arr)
    return str
end

function arr.__concat(lhs, rhs)
    if type(lhs) == 'table' and type(rhs) == 'table' then
        local res = setmetatable({}, getmetatable(lhs) or getmetatable(rhs))
        for i = 1, #lhs do
            res[i] = lhs[i]
        end
        local l = #lhs
        for i = 1, #rhs do
            res[i + l] = rhs[i]
        end
        return res
    else
        return tostring(lhs) .. tostring(rhs)
    end
end

function arr.__unm(array)
    return arr.map(array, function(x) return -x end)
end

local function mathTemplate(lhs, rhs, funName, fun)
    checkTypeMulti('Module:Array.' .. funName, 1, lhs, {'number', 'table'})
    checkTypeMulti('Module:Array.' .. funName, 2, rhs, {'number', 'table'})
    local res = setmetatable({}, getmetatable(lhs) or getmetatable(rhs))

    if type(lhs) == 'number' then
        for i = 1, #rhs do
            res[i] = fun(lhs, rhs[i])
        end
    elseif type(rhs) == 'number' then
        for i = 1, #lhs do
            res[i] = fun(lhs[i], rhs)
        end
    else
        assert(#lhs == #rhs, string.format('Tables are not equal length (lhs=%d, rhs=%d)', #lhs, #rhs))
        for i = 1, #lhs do
            res[i] = fun(lhs[i], rhs[i])
        end
    end

    return res
end

function arr.__add(lhs, rhs)
    return mathTemplate(lhs, rhs, '__add', function(x, y) return x + y end)
end

function arr.__sub(lhs, rhs)
    return mathTemplate(lhs, rhs, '__sub', function(x, y) return x - y end)
end

function arr.__mul(lhs, rhs)
    return mathTemplate(lhs, rhs, '__mul', function(x, y) return x * y end)
end

function arr.__div(lhs, rhs)
    return mathTemplate(lhs, rhs, '__div', function(x, y) return x / y end)
end

function arr.__pow(lhs, rhs)
    return mathTemplate(lhs, rhs, '__pow', function(x, y) return x ^ y end)
end

function arr.__lt(lhs, rhs)
    for i = 1, math.min(#lhs, #rhs) do
        if lhs[i] >= rhs[i] then
            return false
        end
    end
    return true
end

function arr.__le(lhs, rhs)
    for i = 1, math.min(#lhs, #rhs) do
        if lhs[i] > rhs[i] then
            return false
        end
    end
    return true
end

function arr.__eq(lhs, rhs)
    if #lhs ~= #rhs then
        return false
    end
    for i = 1, #lhs do
        if lhs[i] ~= rhs[i] then
            return false
        end
    end
    return true
end

function arr.all(array, fn)
    checkType('Module:Array.all', 1, array, '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
    local i = 1
    while array[i] ~= nil do
        if not fn(array[i], i) then
            return false
        end
        i = i + 1
    end
    return true
end

function arr.any(array, fn)
    checkType('Module:Array.any', 1, array, '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
    local i = 1
    while array[i] ~= nil do
        if fn(array[i], i) then
            return true
        end
        i = i + 1
    end
    return false
end

function arr.clean(array)
    checkType('Module:Array.clean', 1, array, 'table')
    for i = 1, #array do
        if type(array[i]) == 'table' then
            arr.clean(array[i])
        end
    end
    setmetatable(array, nil)
    return array
end

function arr.contains(array, elem, useElemTableContent)
    checkType('Module:Array.contains', 1, array, 'table')
    if type(elem) == 'table' and useElemTableContent ~= false then
        local elemMap = {}
        local isFound = {}
        arr.each(elem, function(x, i) elemMap[x] = i; isFound[i] = false end)
        for i = 1, #array do
            local j = elemMap[array[i]]
            if j then
                isFound[j] = true
            end
        end
        return arr.all(isFound, true)
    else
        return arr.any(array, function(item) return item == elem end)
    end
end

function arr.count(array, fn)
    checkType('Module:Array.count', 1, array, '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
    local count = 0
    for i = 1, #array do
        if fn(array[i]) then
            count = count + 1
        end
    end
    return count
end

function arr.diff(array, order)
    checkType('Module:Array.diff', 1, array, 'table')
    checkType('Module:Array.diff', 2, order, 'number', true)
    local res = setmetatable({}, getmetatable(array))
    for i = 1, #array - 1 do
        res[i] = array[i+1] - array[i]
    end
    if order and order > 1 then
        return arr.diff(res, order - 1)
    end
    return res
end

function arr.each(array, fn)
    checkType('Module:Array.each', 1, array, 'table')
    checkType('Module:Array.each', 2, fn, 'function')
    local i = 1
    while array[i] ~= nil do
        fn(array[i], i)
        i = i + 1
    end
end

function arr.filter(array, fn)
    checkType('Module:Array.filter', 1, array, '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
    local r = setmetatable({}, getmetatable(array))
    local len = 0
    local i = 1
    while array[i] ~= nil do
        if fn(array[i], i) then
            len = len + 1
            r[len] = array[i]
        end
        i = i + 1
    end
    return r
end

function arr.find(array, fn, default)
    checkType('Module:Array.find', 1, array, 'table')
    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
    local i = 1
    while array[i] ~= nil do
        if fn(array[i], i) then
            return array[i], i
        end
        i = i + 1
    end
    return default
end

function arr.find_index(array, fn, default)
    checkType('Module:Array.find_index', 1, array, 'table')
    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
    local i = 1
    while array[i] ~= nil do
        if fn(array[i], i) then
            return i
        end
        i = i + 1
    end
    return default
end

function arr.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
    local obj = {}
    return setmetatable(obj, {
        __call = function() n = n + step return n end,
        __tostring = function() return 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

function arr.int(array, start, stop)
    checkType('Module:Array.int', 1, array, 'table')
    checkType('Module:Array.int', 2, start, 'number', true)
    checkType('Module:Array.int', 3, stop, 'number', true)
    local res = setmetatable({}, getmetatable(array))
    start = start or 1
    stop = stop or #array
    res[1] = array[start]
    for i = 1, stop - start do
        res[i+1] = res[i] + array[start + i]
    end
    return res
end

function arr.intersect(array1, array2)
    checkType('Module:Array.intersect', 1, array1, 'table')
    checkType('Module:Array.intersect', 2, array2, 'table')
    local array2Elements = {}
    local res = setmetatable({}, getmetatable(array1) or getmetatable(array2))
    local len = 0
    arr.each(array2, function(item) array2Elements[item] = true end)
    arr.each(array1, function(item)
        if array2Elements[item] then
            len = len + 1
            res[len] = item
        end
    end)
    return res
end

function arr.intersects(array1, array2)
    checkType('Module:Array.intersects', 1, array1, 'table')
    checkType('Module:Array.intersects', 2, array2, 'table')
    local small = {}
    local large
    if #array1 <= #array2 then
        arr.each(array1, function(item) small[item] = true end)
        large = array2
    else
        arr.each(array2, function(item) small[item] = true end)
        large = array1
    end
    return arr.any(large, function(item) return small[item] end)
end

function arr.insert(array, val, index, unpackVal)
    checkType('Module:Array.insert', 1, array, 'table')
    checkType('Module:Array.insert', 3, index, 'number', true)
    checkType('Module:Array.insert', 4, unpackVal, 'boolean', true)
    local len = #array
    index = index or (len + 1)

    if type(val) == 'table' and unpackVal ~= false then
        local len2 = #val
        for i = 0, len - index do
            array[len + len2 - i] = array[len - i]
        end
        for i = 0, len2 - 1 do
            array[index + i] = val[i + 1]
        end
    else
        table.insert(array, index, val)
    end

    return array
end

function arr.map(array, fn)
    checkType('Module:Array.map', 1, array, 'table')
    checkType('Module:Array.map', 2, fn, 'function')
    local len = 0
    local r = setmetatable({}, getmetatable(array))
    local i = 1
    while array[i] ~= nil do
        local tmp = fn(array[i], i)
        if tmp ~= nil then
            len = len + 1
            r[len] = tmp
        end
        i = i + 1
    end
    return r
end

function arr.max_by(array, fn)
    checkType('Module:Array.max_by', 1, array, 'table')
    checkType('Module:Array.max_by', 2, fn, 'function')
    return unpack(arr.reduce(array, function(new, old, i)
        local y = fn(new)
        return y > old[2] and {new, y, i} or old
    end, {nil, -math.huge}))
end

function arr.max(array)
    checkType('Module:Array.max', 1, array, 'table')
    local val, _, i = arr.max_by(array, function(x) return x end)
    return val, i
end

function arr.min(array)
    checkType('Module:Array.min', 1, array, 'table')
    local val, _, i = arr.max_by(array, function(x) return -x end)
    return val, i
end

function arr.new(array)
    array = array or {}
    for _, v in pairs(array) do
        if type(v) == 'table' then
            arr.new(v)
        end
    end

    if getmetatable(array) == nil then
        setmetatable(array, arr)
    end

    return array
end

function arr.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 array = setmetatable({}, arr)
    local len = 0
    if not stop then
        stop = start
        start = 1
    end
    for i = start, stop, step or 1 do
        len = len + 1
        array[len] = i
    end
    return array
end

function arr.reduce(array, fn, accumulator)
    checkType('Module:Array.reduce', 1, array, 'table')
    checkType('Module:Array.reduce', 2, fn, 'function')
    local acc = accumulator
    local i = 1
    if acc == nil then
        acc = array[1]
        i = 2
    end
    while array[i] ~= nil do
        acc = fn(array[i], acc, i)
        i = i + 1
    end
    return acc
end

function arr.reject(array, fn)
    checkType('Module:Array.reject', 1, array, 'table')
    checkTypeMulti('Module:Array.reject', 2, fn, {'function', 'table', 'number', 'boolean'})
    if fn == nil then fn = function(item) return item end end
    if type(fn) ~= 'function' and type(fn) ~= 'table' then
        fn = {fn}
    end
    local r = setmetatable({}, getmetatable(array))
    local len = 0
    if type(fn) == 'function' then
        local i = 1
        while array[i] ~= nil do
            if not fn(array[i], i) then
                len = len + 1
                r[len] = array[i]
            end
            i = i + 1
        end
    else
        local rejectMap = {}
        arr.each(fn, function(item) rejectMap[item] = true end)
        local i = 1
        while array[i] ~= nil do
            if not rejectMap[array[i]] then
                len = len + 1
                r[len] = array[i]
            end
            i = i + 1
        end
    end
    return r
end

function arr.rep(val, n)
    checkType('Module:Array.rep', 2, n, 'number')
    local r = setmetatable({}, arr)
    for i = 1, n do
        r[i] = val
    end
    return r
end

function arr.scan(array, fn, accumulator)
    checkType('Module:Array.scan', 1, array, 'table')
    checkType('Module:Array.scan', 2, fn, 'function')
    local acc = accumulator
    local r = setmetatable({}, getmetatable(array))
    local i = 1
    while array[i] ~= nil do
        if i == 1 and not accumulator then
            acc = array[i]
        else
            acc = fn(array[i], acc)
        end
        r[i] = acc
        i = i + 1
    end
    return r
end

function arr.slice(array, start, finish)
    checkType('Module:Array.slice', 1, array, 'table')
    checkType('Module:Array.slice', 2, start, 'number', true)
    checkType('Module:Array.slice', 3, finish, 'number', true)
    start = start or 1
    finish = finish or #array
    if start < 0 and finish == nil then
        finish = #array + start
        start = 1
    elseif start < 0 then
        start = #array + start
    end
    if finish < 0 then
        finish = #array + finish
    end
    local r = setmetatable({}, getmetatable(array))
    local len = 0
    for i = start, finish do
        len = len + 1
        r[len] = array[i]
    end
    return r
end

function arr.split(array, count)
    checkType('Module:Array.split', 1, array, 'table')
    checkType('Module:Array.split', 2, count, 'number')
    local x = setmetatable({}, getmetatable(array))
    local y = setmetatable({}, getmetatable(array))
    for i = 1, #array do
        table.insert(i <= count and x or y, array[i])
    end
    return x, y
end

function arr.sum(array)
    checkType('Module:Array.sum', 1, array, 'table')
    local res = 0
    for i = 1, #array do
        res = res + array[i]
    end
    return res
end

function arr.take(array, count, offset)
    checkType('Module:Array.take', 1, array, 'table')
    checkType('Module:Array.take', 2, count, 'number')
    checkType('Module:Array.take', 3, offset, 'number', true)
    local x = setmetatable({}, getmetatable(array))
    for i = offset or 1, #array do
        if i <= count then
            table.insert(x, array[i])
        end
    end
    return x
end

function arr.take_every(array, n, offset)
    checkType('Module:Array.take_every', 1, array, 'table')
    checkType('Module:Array.take_every', 2, n, 'number')
    checkType('Module:Array.take_every', 3, offset, 'number', true)
    local r = setmetatable({}, getmetatable(array))
    local len = 0
    local i = offset or 1
    while array[i] ~= nil do
        len = len + 1
        r[len] = array[i]
        i = i + n
    end
    return r
end

function arr.unique(array, fn)
    checkType('Module:Array.unique', 1, array, 'table')
    checkType('Module:Array.unique', 2, fn, 'function', true)
    fn = fn or function(item) return item end
    local r = setmetatable({}, getmetatable(array))
    local len = 0
    local hash = {}
    local i = 1
    while array[i] ~= nil do
        local id = fn(array[i])
        if not hash[id] then
            len = len + 1
            r[len] = array[i]
            hash[id] = true
        end
        i = i + 1
    end
    return r
end

function arr.update(array, indexes, values)
    checkType('Module:Array.update', 1, array, 'table')
    checkTypeMulti('Module:Array.update', 2, indexes, {'table', 'number'})
    if type(indexes) == 'number' then
        indexes = {indexes}
    end
    if type(values) == 'table' then
        assert(#indexes == #values, 'Values array must be of equal length as index array')
        for i = 1, #indexes do
            array[indexes[i]] = values[i]
        end
    else
        for i = 1, #indexes do
            array[indexes[i]] = values
        end
    end
    return array
end

function arr.zip(...)
    local arrays = { ... }
    checkType('Module:Array.zip', 1, arrays[1], 'table')
    local r = setmetatable({}, getmetatable(arrays[1]))
    local _, longest = arr.max_by(arrays, function(array) return #array end)
    for i = 1, longest do
        local q = {}
        for j = 1, #arrays do
            table.insert(q, arrays[j][i])
        end
        table.insert(r, q)
    end
    return r
end

return arr
-- </nowiki>