Module:FunList: Difference between revisions
From Melvor Idle
No edit summary |
(Define metatable outside of ctor) |
||
(35 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
local funlist = {} | local funlist = {} | ||
funlist. | local funlist_mt = { | ||
__index = funlist, | |||
__pairs = function(t) return pairs(t.mytable) end, | |||
__ipairs = function(t) return ipairs(t.mytable) end | |||
} | |||
-- Constructor | -- Constructor | ||
function funlist.new(tbl) | function funlist.new(tbl) | ||
assert(type(tbl) == 'table') | assert(type(tbl) == 'table', 'Value must be a non-nil table.') | ||
local self = setmetatable({}, funlist_mt) | |||
self.mytable = tbl | |||
self.sortSelectors = {} | |||
return self | |||
end | |||
-- Private functions. | |||
local function getElementAtIndex(tbl, index) | |||
assert(tonumber(index)) | |||
local ix = tonumber(index) | |||
local i = 0 | |||
if ix >= 0 then | |||
for _, v in pairs(tbl) do | |||
i = i + 1 | |||
if i == ix then | |||
return true, v | |||
end | |||
end | |||
end | |||
return false, nil | |||
end | |||
-- Public functions | |||
-- Determines whether all elements of a sequence satisfy a condition. | |||
function funlist:all(predicate) | |||
assert(predicate) | |||
for _, v in pairs(self.mytable) do | |||
if predicate(v) == false then | |||
return false | |||
end | end | ||
end | end | ||
return true | |||
end | |||
-- Determines whether any element of a sequence exists or satisfies a condition. | |||
function funlist:any(predicate) | |||
if predicate then | |||
for _, v in pairs(self.mytable) do | |||
for _, v in pairs(mytable) do | if predicate(v) == true then | ||
if predicate(v) == | return true | ||
return | |||
end | end | ||
end | end | ||
return true | else | ||
for _, _ in pairs(self.mytable) do | |||
return true | |||
end | |||
end | end | ||
return false | |||
end | |||
-- Appends a value to the end of the sequence. | |||
function funlist:append(item) | |||
assert(item) | |||
table.insert(self.mytable, item) | |||
-- Determines whether | return self | ||
end | |||
-- Determines whether a sequence contains a specified element. | |||
function funlist:contains(item) | |||
assert(item) | |||
for _, v in pairs(self.mytable) do | |||
if self:isEqual(v, item) == true then | |||
return true | |||
end | end | ||
end | end | ||
return false | |||
end | |||
-- Returns the number of elements in a sequence. | |||
function funlist:count() | |||
local count = 0 | |||
for _, _ in pairs(self.mytable) do | |||
count = count + 1 | |||
end | |||
return count | |||
end | |||
-- Returns the element at a specified index in a sequence. | |||
function funlist:itemAt(index) | |||
local found, item = getElementAtIndex(self.mytable, index) | |||
if found == true then | |||
return item | |||
end | end | ||
-- | error('Index out of range') | ||
end | |||
for _, v in pairs(mytable) do | -- Returns the element at a specified index in a sequence or a default value if the index is out of range. | ||
if | function funlist:itemAtOrDefault(index, defaultItem) | ||
return | local found, item = getElementAtIndex(self.mytable, index) | ||
if found == true then | |||
return item | |||
end | |||
return defaultItem | |||
end | |||
-- Returns the first element of a sequence. | |||
function funlist:first(predicate) | |||
if predicate then | |||
for _, v in pairs(self.mytable) do | |||
if predicate(v) == true then | |||
return v | |||
end | end | ||
end | end | ||
return | else | ||
for _, v in pairs(self.mytable) do | |||
return v | |||
end | |||
end | end | ||
error('No items in sequence.') | |||
end | |||
-- Returns the first element of a sequence, or a default value if no element is found. | |||
for _, | function funlist:firstOrDefault(predicate, defaultItem) | ||
if predicate then | |||
for _, v in pairs(self.mytable) do | |||
if predicate(v) == true then | |||
return v | |||
end | |||
end | |||
else | |||
for _, v in pairs(self.mytable) do | |||
return v | |||
end | end | ||
end | end | ||
return defaultItem | |||
-- Returns the element | end | ||
function funlist: | |||
local | -- Returns the last element of a sequence. | ||
if | function funlist:last() | ||
return | if self:any() == false then | ||
error('Sequence contains no items.') | |||
end | |||
local last = nil | |||
for _, v in pairs(self.mytable) do | |||
last = v | |||
end | |||
return last | |||
end | |||
-- Returns the last element of a sequence, or a default value if no element is found. | |||
function funlist:lastOrDefault(defaultItem) | |||
if self:any() == false then | |||
return defaultItem | |||
end | |||
local last = nil | |||
for _, v in pairs(self.mytable) do | |||
last = v | |||
end | |||
return last | |||
end | |||
-- Returns the maximum value in a sequence of values. | |||
function funlist:max() | |||
local h = self:first() | |||
for _, v in pairs(self.mytable) do | |||
local num = tonumber(v) | |||
if num == nil then error('Value is NaN') end | |||
if num > h then h = num end | |||
end | |||
return h | |||
end | |||
-- Returns the minimum value in a sequence of values. | |||
function funlist:min() | |||
local l = self:first() | |||
for _, v in pairs(self.mytable) do | |||
local num = tonumber(v) | |||
if num == nil then error('Value is NaN') end | |||
if num < l then l = num end | |||
end | |||
return l | |||
end | |||
function funlist:sum() | |||
local total = 0 | |||
for _, v in pairs(self.mytable) do | |||
local num = tonumber(v) | |||
if num == nil then error('Value is NaN') end | |||
total = total + num | |||
end | |||
return total | |||
end | |||
-- Adds elements from one sequence to the other. | |||
function funlist:concat(tbl) | |||
assert(type(tbl) == 'table') | |||
for _, v in pairs(tbl) do | |||
table.insert(self.mytable, v) | |||
end | |||
return self | |||
end | |||
-- Adds elements from one sequence to the other by key. | |||
function funlist:join(tbl) | |||
assert(type(tbl) == 'table') | |||
for k, v in pairs(tbl) do | |||
self.mytable[k] = v | |||
end | |||
return self | |||
end | |||
-- Sorts the internal table by default Lua sorting behaviour | |||
-- Unless specific sorting is requested. | |||
function funlist:sort() | |||
function hasSortData() | |||
for _, _ in pairs(self.sortSelectors) do | |||
return true | |||
end | end | ||
end | end | ||
if hasSortData() == true then | |||
function funlist: | self:executeSort() | ||
local | else | ||
table.sort(self.mytable) | |||
end | |||
return self | |||
end | |||
function funlist:sortDecending() | |||
table.sort(self.mytable, function(left, right) return left > right end) | |||
return self | |||
end | |||
function funlist:executeSort() | |||
local sortFunc = function(left, right) | |||
for _, sortMeta in pairs(self.sortSelectors) do | |||
local selector = sortMeta.selector | |||
local sortAscending = sortMeta.ascending | |||
local leftVal = selector(left) | |||
local rightVal = selector(right) | |||
if leftVal ~= rightVal then | |||
if sortAscending then | |||
return leftVal < rightVal | |||
else | |||
return leftVal > rightVal | |||
end | |||
end | |||
end | |||
end | end | ||
-- | table.sort(self.mytable, sortFunc) | ||
function funlist: | self.sortSelectors = {} | ||
for _, | return self | ||
end | |||
-- Adds a sort condition | |||
function funlist:sortBy(selector) | |||
assert(selector) | |||
table.insert(self.sortSelectors, { | |||
selector = selector, | |||
ascending = true | |||
}) | |||
return self | |||
end | |||
function funlist:sortByDecending(selector) | |||
assert(selector) | |||
table.insert(self.sortSelectors, { | |||
selector = selector, | |||
ascending = false | |||
}) | |||
return self | |||
end | |||
function funlist:thenBy(selector) | |||
return self:sortBy(selector) | |||
end | |||
function funlist:thenByDecending(selector) | |||
return self:sortByDecending(selector) | |||
end | |||
-- Projects each element of a sequence into a new form. | |||
function funlist:select(selector) | |||
assert(selector) | |||
local result = {} | |||
for _, v in pairs(self.mytable) do | |||
local val = selector(v) | |||
assert(val) | |||
table.insert(result, selector(v)) | |||
end | |||
self.mytable = result | |||
return self | |||
end | |||
-- Projects each element of a sequence into a new form and flattens the result. | |||
function funlist:selectMany(selector) | |||
assert(selector) | |||
local result = {} | |||
for _, item in pairs(self.mytable) do | |||
local tbl = selector(item) | |||
-- Resulting object must be a table here! | |||
assert(type(tbl) == 'table') | |||
for _, value in pairs(tbl) do | |||
table.insert(result, value) | |||
end | end | ||
end | end | ||
self.mytable = result | |||
return self | |||
end | |||
-- Filters a sequence of values based on a predicate. | |||
function funlist:where(predicate) | |||
assert(predicate) | |||
local result = {} | |||
for k, v in pairs(self.mytable) do | |||
if predicate(v) then | |||
result[k] = v | |||
end | |||
end | |||
self.mytable = result | |||
return self | |||
end | |||
function funlist:toDictionary(keySelector, valueSelector) | |||
assert(keySelector) | |||
assert(valueSelector) | |||
local dict = {} | |||
for _, item in pairs(self.mytable) do | |||
local key = keySelector(item) | |||
local val = valueSelector(item) | |||
if dict[key] then | |||
error('Duplicate key error.') | |||
end | end | ||
dict[key] = val | |||
end | end | ||
-- | return dict | ||
end | |||
if | |||
function funlist:distinct() | |||
local hashSet = {} | |||
local uniques = {} | |||
-- Filter out unique values. | |||
for _, v in pairs(self.mytable) do | |||
if not hashSet[v] then | |||
hashSet[v] = true | |||
table.insert(uniques, v) | |||
end | end | ||
end | end | ||
self.mytable = uniques | |||
function funlist: | return self | ||
end | |||
function funlist:distinctBy(selector) | |||
assert(selector) | |||
local hashSet = {} | |||
local uniques = {} | |||
-- Filter out unique values. | |||
for _, item in pairs(self.mytable) do | |||
local v = selector(item) | |||
if not hashSet[v] then | |||
hashSet[v] = true | |||
table.insert(uniques, item) | |||
end | end | ||
end | end | ||
self.mytable = uniques | |||
function funlist: | return self | ||
end | |||
function funlist:groupBy(keySelector, valueSelector) | |||
assert(keySelector) | |||
local lookup = {} | |||
-- Filter out unique values. | |||
for _, item in pairs(self.mytable) do | |||
local key = keySelector(item) | |||
local value = valueSelector and valueSelector(item) or item | |||
if not lookup[key] then | |||
lookup[key] = {} | |||
end | |||
table.insert(lookup[key], value) | |||
end | end | ||
self.mytable = lookup | |||
return self | |||
end | |||
function funlist:toTable() | |||
return self.mytable | |||
end | end | ||
Latest revision as of 15:51, 20 July 2024
Documentation for this module may be created at Module:FunList/doc
local funlist = {}
local funlist_mt = {
__index = funlist,
__pairs = function(t) return pairs(t.mytable) end,
__ipairs = function(t) return ipairs(t.mytable) end
}
-- Constructor
function funlist.new(tbl)
assert(type(tbl) == 'table', 'Value must be a non-nil table.')
local self = setmetatable({}, funlist_mt)
self.mytable = tbl
self.sortSelectors = {}
return self
end
-- Private functions.
local function getElementAtIndex(tbl, index)
assert(tonumber(index))
local ix = tonumber(index)
local i = 0
if ix >= 0 then
for _, v in pairs(tbl) do
i = i + 1
if i == ix then
return true, v
end
end
end
return false, nil
end
-- Public functions
-- Determines whether all elements of a sequence satisfy a condition.
function funlist:all(predicate)
assert(predicate)
for _, v in pairs(self.mytable) do
if predicate(v) == false then
return false
end
end
return true
end
-- Determines whether any element of a sequence exists or satisfies a condition.
function funlist:any(predicate)
if predicate then
for _, v in pairs(self.mytable) do
if predicate(v) == true then
return true
end
end
else
for _, _ in pairs(self.mytable) do
return true
end
end
return false
end
-- Appends a value to the end of the sequence.
function funlist:append(item)
assert(item)
table.insert(self.mytable, item)
return self
end
-- Determines whether a sequence contains a specified element.
function funlist:contains(item)
assert(item)
for _, v in pairs(self.mytable) do
if self:isEqual(v, item) == true then
return true
end
end
return false
end
-- Returns the number of elements in a sequence.
function funlist:count()
local count = 0
for _, _ in pairs(self.mytable) do
count = count + 1
end
return count
end
-- Returns the element at a specified index in a sequence.
function funlist:itemAt(index)
local found, item = getElementAtIndex(self.mytable, index)
if found == true then
return item
end
error('Index out of range')
end
-- Returns the element at a specified index in a sequence or a default value if the index is out of range.
function funlist:itemAtOrDefault(index, defaultItem)
local found, item = getElementAtIndex(self.mytable, index)
if found == true then
return item
end
return defaultItem
end
-- Returns the first element of a sequence.
function funlist:first(predicate)
if predicate then
for _, v in pairs(self.mytable) do
if predicate(v) == true then
return v
end
end
else
for _, v in pairs(self.mytable) do
return v
end
end
error('No items in sequence.')
end
-- Returns the first element of a sequence, or a default value if no element is found.
function funlist:firstOrDefault(predicate, defaultItem)
if predicate then
for _, v in pairs(self.mytable) do
if predicate(v) == true then
return v
end
end
else
for _, v in pairs(self.mytable) do
return v
end
end
return defaultItem
end
-- Returns the last element of a sequence.
function funlist:last()
if self:any() == false then
error('Sequence contains no items.')
end
local last = nil
for _, v in pairs(self.mytable) do
last = v
end
return last
end
-- Returns the last element of a sequence, or a default value if no element is found.
function funlist:lastOrDefault(defaultItem)
if self:any() == false then
return defaultItem
end
local last = nil
for _, v in pairs(self.mytable) do
last = v
end
return last
end
-- Returns the maximum value in a sequence of values.
function funlist:max()
local h = self:first()
for _, v in pairs(self.mytable) do
local num = tonumber(v)
if num == nil then error('Value is NaN') end
if num > h then h = num end
end
return h
end
-- Returns the minimum value in a sequence of values.
function funlist:min()
local l = self:first()
for _, v in pairs(self.mytable) do
local num = tonumber(v)
if num == nil then error('Value is NaN') end
if num < l then l = num end
end
return l
end
function funlist:sum()
local total = 0
for _, v in pairs(self.mytable) do
local num = tonumber(v)
if num == nil then error('Value is NaN') end
total = total + num
end
return total
end
-- Adds elements from one sequence to the other.
function funlist:concat(tbl)
assert(type(tbl) == 'table')
for _, v in pairs(tbl) do
table.insert(self.mytable, v)
end
return self
end
-- Adds elements from one sequence to the other by key.
function funlist:join(tbl)
assert(type(tbl) == 'table')
for k, v in pairs(tbl) do
self.mytable[k] = v
end
return self
end
-- Sorts the internal table by default Lua sorting behaviour
-- Unless specific sorting is requested.
function funlist:sort()
function hasSortData()
for _, _ in pairs(self.sortSelectors) do
return true
end
end
if hasSortData() == true then
self:executeSort()
else
table.sort(self.mytable)
end
return self
end
function funlist:sortDecending()
table.sort(self.mytable, function(left, right) return left > right end)
return self
end
function funlist:executeSort()
local sortFunc = function(left, right)
for _, sortMeta in pairs(self.sortSelectors) do
local selector = sortMeta.selector
local sortAscending = sortMeta.ascending
local leftVal = selector(left)
local rightVal = selector(right)
if leftVal ~= rightVal then
if sortAscending then
return leftVal < rightVal
else
return leftVal > rightVal
end
end
end
end
table.sort(self.mytable, sortFunc)
self.sortSelectors = {}
return self
end
-- Adds a sort condition
function funlist:sortBy(selector)
assert(selector)
table.insert(self.sortSelectors, {
selector = selector,
ascending = true
})
return self
end
function funlist:sortByDecending(selector)
assert(selector)
table.insert(self.sortSelectors, {
selector = selector,
ascending = false
})
return self
end
function funlist:thenBy(selector)
return self:sortBy(selector)
end
function funlist:thenByDecending(selector)
return self:sortByDecending(selector)
end
-- Projects each element of a sequence into a new form.
function funlist:select(selector)
assert(selector)
local result = {}
for _, v in pairs(self.mytable) do
local val = selector(v)
assert(val)
table.insert(result, selector(v))
end
self.mytable = result
return self
end
-- Projects each element of a sequence into a new form and flattens the result.
function funlist:selectMany(selector)
assert(selector)
local result = {}
for _, item in pairs(self.mytable) do
local tbl = selector(item)
-- Resulting object must be a table here!
assert(type(tbl) == 'table')
for _, value in pairs(tbl) do
table.insert(result, value)
end
end
self.mytable = result
return self
end
-- Filters a sequence of values based on a predicate.
function funlist:where(predicate)
assert(predicate)
local result = {}
for k, v in pairs(self.mytable) do
if predicate(v) then
result[k] = v
end
end
self.mytable = result
return self
end
function funlist:toDictionary(keySelector, valueSelector)
assert(keySelector)
assert(valueSelector)
local dict = {}
for _, item in pairs(self.mytable) do
local key = keySelector(item)
local val = valueSelector(item)
if dict[key] then
error('Duplicate key error.')
end
dict[key] = val
end
return dict
end
function funlist:distinct()
local hashSet = {}
local uniques = {}
-- Filter out unique values.
for _, v in pairs(self.mytable) do
if not hashSet[v] then
hashSet[v] = true
table.insert(uniques, v)
end
end
self.mytable = uniques
return self
end
function funlist:distinctBy(selector)
assert(selector)
local hashSet = {}
local uniques = {}
-- Filter out unique values.
for _, item in pairs(self.mytable) do
local v = selector(item)
if not hashSet[v] then
hashSet[v] = true
table.insert(uniques, item)
end
end
self.mytable = uniques
return self
end
function funlist:groupBy(keySelector, valueSelector)
assert(keySelector)
local lookup = {}
-- Filter out unique values.
for _, item in pairs(self.mytable) do
local key = keySelector(item)
local value = valueSelector and valueSelector(item) or item
if not lookup[key] then
lookup[key] = {}
end
table.insert(lookup[key], value)
end
self.mytable = lookup
return self
end
function funlist:toTable()
return self.mytable
end
-- Helper function to check if objects are equal.
function funlist.isEqual(obj1, obj2)
local type1 = type(obj1)
local type2 = type(obj2)
if type1 ~= type2 then
return false
end
if type1 == "number" or type1 == "string" or type1 == "boolean" then
return obj1 == obj2
elseif type1 == "table" then
if #obj1 ~= #obj2 then
return false
end
for k, v in pairs(obj1) do
if not isEqual(v, obj2[k]) then
return false
end
end
return true
else
return obj1 == obj2
end
end
return funlist