Module:FunList: Difference between revisions

From Melvor Idle
No edit summary
(Define metatable outside of ctor)
 
(38 intermediate revisions by the same user not shown)
Line 1: Line 1:
local funlist = {}
local funlist = {}
funlist.__index = 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.')
    assert(tbl ~= nil)
local self = setmetatable({}, funlist_mt)
      
      
    local self = setmetatable({}, funlist)
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
-- Private fields
return false, nil
local mytable = tbl
end
 
-- Private functions.
-- Public functions
local function getElementAtIndex(index)
-- Determines whether all elements of a sequence satisfy a condition.
assert(tonumber(index))
function funlist:all(predicate)
local ix = tonumber(index)
assert(predicate)
local i = 0
for _, v in pairs(self.mytable) do
if ix >= 0 then
if predicate(v) == false then
for _, v in pairs(mytable) do
return false
i = i + 1
if i == ix then
return true, v
end
end
end
end
return false, nil
end
end
return true
end


-- Public functions
-- Determines whether any element of a sequence exists or satisfies a condition.
-- Determines whether all elements of a sequence satisfy a condition.
function funlist:any(predicate)
function funlist.all(predicate)
if predicate then
assert(predicate)
for _, v in pairs(self.mytable) do
for _, v in pairs(mytable) do
if predicate(v) == true then
if predicate(v) == false then
return true
return false
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 any element of a sequence exists or satisfies a condition.
return self
function funlist.any(predicate)
end
if predicate then
 
for _, v in pairs(mytable) do
-- Determines whether a sequence contains a specified element.
if predicate(v) == true then
function funlist:contains(item)
return true
assert(item)
end
for _, v in pairs(self.mytable) do
end
if self:isEqual(v, item) == true then
else
return true
for _, _ in pairs(mytable) do
return true
end
end
end
return false
end
end
return false
end


-- Appends a value to the end of the sequence.
-- Returns the number of elements in a sequence.
function funlist.append(item)
function funlist:count()
assert(item)
local count = 0
table.insert(mytable, item)
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
-- Determines whether a sequence contains a specified element.
error('Index out of range')
function funlist.contains(item)
end
assert(item)
 
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 funlist.isEqual(v, item) == true then
function funlist:itemAtOrDefault(index, defaultItem)
return true
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
end
else
for _, v in pairs(self.mytable) do
return v
end
end
return false
end
end
return defaultItem
-- Returns the number of elements in a sequence.
end
function funlist.count()
 
local count = 0
-- Returns the last element of a sequence.
for _, _ in pairs(mytable) do
function funlist:last()
count = count + 1
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
return count
end
end
-- Returns the element at a specified index in a sequence.
if hasSortData() == true then
function funlist.itemAt(index)
self:executeSort()
local found, item = getElementAtIndex(index)
else
if found == true then
table.sort(self.mytable)
return item
end
end
return self
end
error('Index out of range')
 
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
-- Returns the element at a specified index in a sequence or a default value if the index is out of range.
table.sort(self.mytable, sortFunc)
function funlist.itemAtOrDefault(index, defaultItem)
self.sortSelectors = {}
local found, item = getElementAtIndex(index)
return self
if found == true then
end
return item
 
-- 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
return defaultItem
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)
-- Returns the first element of a sequence.
local dict = {}
function funlist.first()
for _, item in pairs(self.mytable) do
for _, v in pairs(mytable) do
local key = keySelector(item)
return v
local val = valueSelector(item)
if dict[key] then
error('Duplicate key error.')
end
end
error('No items in sequence.')
dict[key] = val
end
end
 
-- Returns the first element of a sequence, or a default value if no element is found.
return dict
function funlist.firstOrDefault(defaultItem)
end
for _, v in pairs(mytable) do
 
return v
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
return defaultItem
end
end
-- Returns the last element of a sequence.
self.mytable = uniques
function funlist.last()
return self
if funlist.any() == false then
end
error('Sequence contains no items.')
 
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
local last = nil
for _, v in pairs(mytable) do
last = v
end
return last
end
end
-- Returns the last element of a sequence, or a default value if no element is found.
self.mytable = uniques
function funlist.lastOrDefault(defaultItem)
return self
if funlist.any() == false then
end
return defaultItem
 
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
end
local last = nil
 
for _, v in pairs(mytable) do
table.insert(lookup[key], value)
last = v
end
return last
end
end
    return self
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