Module:Array: Difference between revisions

osrsw>CephHunter
Update
m 16 revisions imported
 
(2 intermediate revisions by one other user not shown)
Line 2: Line 2:
local checkType = libraryUtil.checkType
local checkType = libraryUtil.checkType
local checkTypeMulti = libraryUtil.checkTypeMulti
local checkTypeMulti = libraryUtil.checkTypeMulti
---Returns the length of the array but it also works on proxy arrays
---@param arr any[]
---@return integer
local function len(arr)
local l = #arr
if l == 0 then
if arr[1] ~= nil then
-- Exponential search to find length of proxy table
local low = 1
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
else
return l
end
end


---@class Array
---@class Array
Line 14: Line 45:
---@operator pow(number|number[]|Array): Array
---@operator pow(number|number[]|Array): Array
local Array = {
local Array = {
pop = table.remove
pop = table.remove,
len = len
}
}
Array.__index = Array
Array.__index = Array
Line 24: Line 56:
end
end
})
})
-- function Array.__tostring(arr)
-- -- local dumpObject = require('Module:Logger').dumpObject
-- require 'log'
-- local dumpObject = dumpObject
-- local mt = getmetatable(arr)
-- setmetatable(arr, nil)
-- local str = dumpObject(arr, {clean=true, collapseLimit=100})
-- setmetatable(arr, mt)
-- return str
-- end


function Array.__concat(lhs, rhs)
function Array.__concat(lhs, rhs)
if type(lhs) == 'table' and type(rhs) == 'table' then
if type(lhs) == 'table' and type(rhs) == 'table' then
local res = {}
local res = {}
for i = 1, #lhs do
local l1 = len(lhs)
for i = 1, l1 do
res[i] = lhs[i]
res[i] = lhs[i]
end
end
local l = #lhs
for i = 1, len(rhs) do
for i = 1, #rhs do
res[l1 + i] = rhs[i]
res[i + l] = rhs[i]
end
end
return setmetatable(res, getmetatable(lhs) or getmetatable(rhs))
return setmetatable(res, getmetatable(lhs) or getmetatable(rhs))
Line 59: Line 80:
---@param rhs number|number[]|Array
---@param rhs number|number[]|Array
---@param funName string
---@param funName string
---@param opName string
---@param fun fun(lhs: number, rhs: number): number
---@param fun fun(lhs: number, rhs: number): number
---@return Array
---@return Array
local function mathTemplate(lhs, rhs, funName, fun)
local function mathTemplate(lhs, rhs, funName, opName, fun)
checkTypeMulti('Module:Array.' .. funName, 1, lhs, {'number', 'table'})
checkTypeMulti('Module:Array.' .. funName, 1, lhs, {'number', 'table'})
checkTypeMulti('Module:Array.' .. funName, 2, rhs, {'number', 'table'})
checkTypeMulti('Module:Array.' .. funName, 2, rhs, {'number', 'table'})
Line 67: Line 89:


if type(lhs) == 'number' then
if type(lhs) == 'number' then
for i = 1, #rhs do
for i = 1, len(rhs) do
res[i] = fun(lhs, rhs[i])
res[i] = fun(lhs, rhs[i])
end
end
elseif type(rhs) == 'number' then
elseif type(rhs) == 'number' then
for i = 1, #lhs do
for i = 1, len(lhs) do
res[i] = fun(lhs[i], rhs)
res[i] = fun(lhs[i], rhs)
end
end
else
else
assert(#lhs == #rhs, string.format('Tables are not equal length (lhs=%d, rhs=%d)', #lhs, #rhs))
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, #lhs do
for i = 1, len(lhs) do
res[i] = fun(lhs[i], rhs[i])
res[i] = fun(lhs[i], rhs[i])
end
end
Line 85: Line 107:


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


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


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


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


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


function Array.__eq(lhs, rhs)
function Array.__eq(lhs, rhs)
if #lhs ~= #rhs then
if len(lhs) ~= len(rhs) then
return false
return false
end
end
for i = 1, #lhs do
for i = 1, len(lhs) do
if lhs[i] ~= rhs[i] then
if lhs[i] ~= rhs[i] then
return false
return false
Line 130: Line 152:
fn = function(item) return item == val end
fn = function(item) return item == val end
end
end
local i = 1
for i = 1, len(arr) do
while arr[i] ~= nil do
---@diagnostic disable-next-line: redundant-parameter
---@diagnostic disable-next-line: redundant-parameter
if not fn(arr[i], i) then
if not fn(arr[i], i) then
return false
return false
end
end
i = i + 1
end
end
return true
return true
Line 155: Line 175:
fn = function(item) return item == val end
fn = function(item) return item == val end
end
end
local i = 1
for i = 1, len(arr) do
while arr[i] ~= nil do
---@diagnostic disable-next-line: redundant-parameter
---@diagnostic disable-next-line: redundant-parameter
if fn(arr[i], i) then
if fn(arr[i], i) then
return true
return true
end
end
i = i + 1
end
end
return false
return false
Line 171: Line 189:
function Array.clean(arr)
function Array.clean(arr)
checkType('Module:Array.clean', 1, arr, 'table')
checkType('Module:Array.clean', 1, arr, 'table')
for i = 1, #arr do
for i = 1, len(arr) do
if type(arr[i]) == 'table' then
if type(arr[i]) == 'table' then
Array.clean(arr[i])
Array.clean(arr[i])
Line 189: Line 207:
checkType('Module:Array.clone', 2, deep, 'boolean', true)
checkType('Module:Array.clone', 2, deep, 'boolean', true)
local res = {}
local res = {}
for i = 1, #arr do
for i = 1, len(arr) do
if deep == true and type(arr[i]) == 'table' then
if deep == true and type(arr[i]) == 'table' then
res[i] = Array.clone(arr[i], true)
res[i] = Array.clone(arr[i], true)
Line 205: Line 223:
function Array.contains(arr, val)
function Array.contains(arr, val)
checkType('Module:Array.contains', 1, arr, 'table')
checkType('Module:Array.contains', 1, arr, 'table')
for i = 1, #arr do
for i = 1, len(arr) do
if arr[i] == val then
if arr[i] == val then
return true
return true
Line 221: Line 239:
checkType('Module:Array.containsAny', 2, t, 'table')
checkType('Module:Array.containsAny', 2, t, 'table')
local lookupTbl = {}
local lookupTbl = {}
for i = 1, #t do
for i = 1, len(t) do
lookupTbl[t[i]] = true
lookupTbl[t[i]] = true
end
end
for i = 1, #arr do
for i = 1, len(arr) do
if lookupTbl[arr[i]] then
if lookupTbl[arr[i]] then
return true
return true
Line 240: Line 258:
checkType('Module:Array.containsAll', 2, t, 'table')
checkType('Module:Array.containsAll', 2, t, 'table')
local lookupTbl = {}
local lookupTbl = {}
local l = #t
local trueCount = 0
local trueCount = 0
local l = len(t)
for i = 1, l do
for i = 1, l do
lookupTbl[t[i]] = false
lookupTbl[t[i]] = false
end
end
for i = 1, #arr do
for i = 1, len(arr) do
if lookupTbl[arr[i]] == false then
if lookupTbl[arr[i]] == false then
lookupTbl[arr[i]] = true
lookupTbl[arr[i]] = true
Line 266: Line 284:
checkType('Module:Array.convolve', 2, y, 'table')
checkType('Module:Array.convolve', 2, y, 'table')
local z = {}
local z = {}
     local xLen, yLen = #x, #y
     local xLen, yLen = len(x), len(y)
     for j = 1, (xLen + yLen - 1) do
     for j = 1, (xLen + yLen - 1) do
         local sum = 0
         local sum = 0
Line 312: Line 330:
end
end
local count = 0
local count = 0
for i = 1, #arr do
for i = 1, len(arr) do
if val(arr[i]) then
if val(arr[i]) then
count = count + 1
count = count + 1
Line 329: Line 347:
checkType('Module:Array.diff', 2, order, 'number', true)
checkType('Module:Array.diff', 2, order, 'number', true)
local res = {}
local res = {}
for i = 1, #arr - 1 do
for i = 1, len(arr) - 1 do
res[i] = arr[i+1] - arr[i]
res[i] = arr[i+1] - arr[i]
end
end
Line 344: Line 362:
checkType('Module:Array.each', 1, arr, 'table')
checkType('Module:Array.each', 1, arr, 'table')
checkType('Module:Array.each', 2, fn, 'function')
checkType('Module:Array.each', 2, fn, 'function')
local i = 1
for i = 1, len(arr) do
while arr[i] ~= nil do
fn(arr[i], i)
fn(arr[i], i)
i = i + 1
end
end
end
end
Line 360: Line 376:
checkType('Module:Array.filter', 2, fn, 'function')
checkType('Module:Array.filter', 2, fn, 'function')
local r = {}
local r = {}
local len = 0
local l = 0
local i = 1
for i = 1, len(arr) do
while arr[i] ~= nil do
if fn(arr[i], i) then
if fn(arr[i], i) then
len = len + 1
l = l + 1
r[len] = arr[i]
r[l] = arr[i]
end
end
i = i + 1
end
end
return setmetatable(r, getmetatable(arr))
return setmetatable(r, getmetatable(arr))
Line 385: Line 399:
fn = function(item) return item == _val end
fn = function(item) return item == _val end
end
end
local i = 1
for i = 1, len(arr) do
while arr[i] ~= nil do
---@diagnostic disable-next-line: redundant-parameter
---@diagnostic disable-next-line: redundant-parameter
if fn(arr[i], i) then
if fn(arr[i], i) then
return arr[i], i
return arr[i], i
end
end
i = i + 1
end
end
return default, nil
return default, nil
Line 408: Line 420:
val = function(item) return item == _val end
val = function(item) return item == _val end
end
end
local i = 1
for i = 1, len(arr) do
while arr[i] ~= nil do
---@diagnostic disable-next-line: redundant-parameter
---@diagnostic disable-next-line: redundant-parameter
if val(arr[i], i) then
if val(arr[i], i) then
return i
return i
end
end
i = i + 1
end
end
return default
return default
Line 431: Line 441:
end
end
local res = {}
local res = {}
for i = 1, #indexes do
for i = 1, len(indexes) do
res[i] = arr[indexes[i]]
res[i] = arr[indexes[i]]
end
end
Line 449: Line 459:
local res = {}
local res = {}
start = start or 1
start = start or 1
stop = stop or #arr
stop = stop or len(arr)
res[1] = arr[start]
res[1] = arr[start]
for i = 1, stop - start do
for i = 1, stop - start do
Line 467: Line 477:
local arr2Elements = {}
local arr2Elements = {}
local res = {}
local res = {}
local len = 0
local l = 0
Array.each(arr2, function(item) arr2Elements[item] = true end)
Array.each(arr2, function(item) arr2Elements[item] = true end)
Array.each(arr1, function(item)
Array.each(arr1, function(item)
if arr2Elements[item] then
if arr2Elements[item] then
len = len + 1
l = l + 1
res[len] = item
res[l] = item
end
end
end)
end)
Line 487: Line 497:
local small = {}
local small = {}
local large
local large
if #arr1 <= #arr2 then
if len(arr1) <= len(arr2) then
Array.each(arr1, function(item) small[item] = true end)
Array.each(arr1, function(item) small[item] = true end)
large = arr2
large = arr2
Line 512: Line 522:
unpackVal, index = index, nil
unpackVal, index = index, nil
end
end
local len = #arr
local l = len(arr)
index = index or (len + 1)
index = index or (l + 1)
local mt = getmetatable(arr)
local mt = getmetatable(arr)
setmetatable(arr, nil)
setmetatable(arr, nil)


if type(val) == 'table' and unpackVal then
if unpackVal and type(val) == 'table' then
local len2 = #val
local l2 = len(val)
for i = 0, len - index do
for i = 0, l - index do
arr[len + len2 - i] = arr[len - i]
arr[l + l2 - i] = arr[l - i]
end
end
for i = 0, len2 - 1 do
for i = 0, l2 - 1 do
arr[index + i] = val[i + 1]
arr[index + i] = val[i + 1]
end
end
Line 539: Line 549:
checkType('Module:Array.last', 1, arr, 'table')
checkType('Module:Array.last', 1, arr, 'table')
checkType('Module:Array.last', 2, offset, 'number', true)
checkType('Module:Array.last', 2, offset, 'number', true)
return arr[#arr + offset]
return arr[len(arr) + offset]
end
end


Line 550: Line 560:
checkType('Module:Array.map', 1, arr, 'table')
checkType('Module:Array.map', 1, arr, 'table')
checkType('Module:Array.map', 2, fn, 'function')
checkType('Module:Array.map', 2, fn, 'function')
local len = 0
local l = 0
local r = {}
local r = {}
local i = 1
for i = 1, len(arr) do
while arr[i] ~= nil do
local tmp = fn(arr[i], i)
local tmp = fn(arr[i], i)
if tmp ~= nil then
if tmp ~= nil then
len = len + 1
l = l + 1
r[len] = tmp
r[l] = tmp
end
end
i = i + 1
end
end
return setmetatable(r, getmetatable(arr))
return setmetatable(r, getmetatable(arr))
Line 665: Line 673:
return setmetatable(obj, {
return setmetatable(obj, {
__call = function() n = n + step return n end,
__call = function() n = n + step return n end,
__tostring = function() return n end,
__tostring = function() return tostring(n) end,
__index = function() return n end,
__index = function() return n end,
__newindex = function(self, k, v)
__newindex = function(self, k, v)
Line 718: Line 726:
checkType('Module:Array.reduce', 2, fn, 'function')
checkType('Module:Array.reduce', 2, fn, 'function')
local acc = accumulator
local acc = accumulator
local i = 1
local start = 1
if acc == nil then
if acc == nil then
acc = arr[1]
acc = arr[1]
i = 2
start = 2
end
end
while arr[i] ~= nil do
for i = start, len(arr) do
acc = fn(arr[i], acc, i)
acc = fn(arr[i], acc, i)
i = i + 1
end
end
return acc
return acc
Line 747: Line 754:
end
end
local r = {}
local r = {}
local len = 0
local l = 0
if type(val) == 'function' then
if type(val) == 'function' then
local i = 1
for i = 1, len(arr) do
while arr[i] ~= nil do
if not val(arr[i], i) then
if not val(arr[i], i) then
len = len + 1
l = l + 1
r[len] = arr[i]
r[l] = arr[i]
end
end
i = i + 1
end
end
else
else
local rejectMap = {}
local rejectMap = {}
Array.each(val --[[@as any[] ]], function(item) rejectMap[item] = true end)
Array.each(val --[[@as any[] ]], function(item) rejectMap[item] = true end)
local i = 1
for i = 1, len(arr) do
while arr[i] ~= nil do
if not rejectMap[arr[i]] then
if not rejectMap[arr[i]] then
len = len + 1
l = l + 1
r[len] = arr[i]
r[l] = arr[i]
end
end
i = i + 1
end
end
end
end
Line 804: Line 807:
local acc = accumulator
local acc = accumulator
local r = {}
local r = {}
local i = 1
for i = 1, len(arr) do
while arr[i] ~= nil do
if i == 1 and not accumulator then
if i == 1 and not accumulator then
acc = arr[i]
acc = arr[i]
Line 812: Line 814:
end
end
r[i] = acc
r[i] = acc
i = i + 1
end
end
return setmetatable(r, getmetatable(arr))
return setmetatable(r, getmetatable(arr))
Line 836: Line 837:
end
end
if type(values) == 'table' then
if type(values) == 'table' then
assert(#indexes == #values, string.format("Module:Array.set: 'indexes' and 'values' arrays are not equal length (#indexes = %d, #values = %d)", #indexes, #values))
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, #indexes do
for i = 1, len(indexes) do
arr[indexes[i]] = values[i]
arr[indexes[i]] = values[i]
end
end
else
else
for i = 1, #indexes do
for i = 1, len(indexes) do
arr[indexes[i]] = values
arr[indexes[i]] = values
end
end
Line 859: Line 860:
checkType('Module:Array.slice', 2, start, 'number', true)
checkType('Module:Array.slice', 2, start, 'number', true)
checkType('Module:Array.slice', 3, stop, 'number', true)
checkType('Module:Array.slice', 3, stop, 'number', true)
start = start or #arr
start = start or len(arr)
if start < 0 then
if start < 0 then
start = #arr + start
start = len(arr) + start
end
end
if stop == nil then
if stop == nil then
Line 868: Line 869:
end
end
if stop < 0 then
if stop < 0 then
stop = #arr + stop
stop = len(arr) + stop
end
end
local r = {}
local r = {}
Line 890: Line 891:
local x = {}
local x = {}
local y = {}
local y = {}
for i = 1, #arr do
for i = 1, len(arr) do
table.insert(i <= index and x or y, arr[i])
table.insert(i <= index and x or y, arr[i])
end
end
Line 902: Line 903:
checkType('Module:Array.sum', 1, arr, 'table')
checkType('Module:Array.sum', 1, arr, 'table')
local res = 0
local res = 0
for i = 1, #arr do
for i = 1, len(arr) do
res = res + arr[i]
res = res + arr[i]
end
end
Line 920: Line 921:
local x = {}
local x = {}
start = start or 1
start = start or 1
for i = start, math.min(#arr, count + start - 1) do
for i = start, math.min(len(arr), count + start - 1) do
table.insert(x, arr[i])
table.insert(x, arr[i])
end
end
Line 930: Line 931:
---local t = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
---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 )      --> x = { 1, 3, 5, 7, 9 }
---local x = arr.take_every( t, 2, 3 )    --> x = { 1, 3, 5 }
---local x = arr.take_every( t, 2, 3 )    --> x = { 3, 5, 7, 9 }
---local x = arr.take_every( t, 2, 3, 2 ) --> x = { 2, 4, 6 }
---local x = arr.take_every( t, 2, 3, 2 ) --> x = { 3, 5 }
--- ```
--- ```
---@generic T: any[]
---@generic T: any[]
---@param arr T
---@param arr T
---@param n integer # Step size.
---@param n integer # Step size.
---@param start? integer # Start index.
---@param count? integer # Max amount of elements to get.
---@param count? integer # Max amount of elements to get.
---@param start? integer # Start index.
---@return T
---@return T
function Array.take_every(arr, n, count, start)
function Array.take_every(arr, n, start, count)
checkType('Module:Array.take_every', 1, arr, 'table')
checkType('Module:Array.take_every', 1, arr, 'table')
checkType('Module:Array.take_every', 2, n, 'number')
checkType('Module:Array.take_every', 2, n, 'number')
checkType('Module:Array.take_every', 3, count, 'number', true)
checkType('Module:Array.take_every', 3, start, 'number', true)
checkType('Module:Array.take_every', 4, start, 'number', true)
checkType('Module:Array.take_every', 4, count, 'number', true)
count = count or #arr
count = count or len(arr)
start = start or 1
local stop = math.min(len(arr), start + n * (count - 1))
local r = {}
local r = {}
local len = 0
local l = 0
local i = start or 1
for i = start, stop, n do
while arr[i] ~= nil and len < count do
l = l + 1
len = len + 1
r[l] = arr[i]
r[len] = arr[i]
i = i + n
end
end
return setmetatable(r, getmetatable(arr))
return setmetatable(r, getmetatable(arr))
Line 966: Line 967:
fn = fn or function(item) return item end
fn = fn or function(item) return item end
local r = {}
local r = {}
local len = 0
local l = 0
local hash = {}
local hash = {}
local i = 1
for i = 1, len(arr) do
while arr[i] ~= nil do
local id = fn(arr[i])
local id = fn(arr[i])
if not hash[id] then
if not hash[id] then
len = len + 1
l = l + 1
r[len] = arr[i]
r[l] = arr[i]
hash[id] = true
hash[id] = true
end
end
i = i + 1
end
end
return setmetatable(r, getmetatable(arr))
return setmetatable(r, getmetatable(arr))
Line 993: Line 992:
checkType('Module:Array.zip', 1, arrs[1], 'table')
checkType('Module:Array.zip', 1, arrs[1], 'table')
local r = {}
local r = {}
local _, longest = Array.max_by(arrs, function(arr) return #arr end)
local _, longest = Array.max_by(arrs, function(arr) return len(arr) end)
for i = 1, longest do
for i = 1, longest do
local q = {}
local q = {}
for j = 1, #arrs do
for j = 1, len(arrs) do
table.insert(q, arrs[j][i])
table.insert(q, arrs[j][i])
end
end
Line 1,013: Line 1,012:
if type(k) == 'table' then
if type(k) == 'table' then
local res = {}
local res = {}
for i = 1, #k do
for i = 1, len(k) do
res[i] = t[k[i]]
res[i] = t[k[i]]
end
end
Line 1,025: Line 1,024:
if type(k) == 'table' then
if type(k) == 'table' then
if type(v) == 'table' then
if type(v) == 'table' then
for i = 1, #k do
for i = 1, len(k) do
t[k[i]] = v[i]
t[k[i]] = v[i]
end
end
else
else
for i = 1, #k do
for i = 1, len(k) do
t[k[i]] = v
t[k[i]] = v
end
end