Module:FunList/EnumerableExtensions

< Module:FunList
Revision as of 19:38, 26 July 2024 by Ricewind (talk | contribs) (Created page with "local Enumerable = require('Module:Enumerable') local Iterators = require('Module:Iterators') -- Helper function to check if objects are equal. local function 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 retur...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Functions

all

-- Determines whether all elements of a sequence satisfy a condition.
---@param self Enumerable
---@param predicate fun(item: any): boolean
---@return boolean
function Enumerable:all(predicate)

any

-- Determines whether any element of a sequence exists or satisfies a condition.
---@param self Enumerable
---@param predicate fun(item: any): boolean
---@return boolean
function Enumerable:any(predicate)

append

-- Adds an item to the end of the enumerable.
---@param self Enumerable
---@param item any
function Enumerable:append(item)

prepend

-- Adds an item to the front of the enumerable.
---@param self Enumerable
---@param item any
function Enumerable:prepend(item)

contains

-- Determines whether a sequence contains a specified element.
---@param self Enumerable
---@param item any
---@return boolean
function Enumerable:contains(item)

count

-- Returns the number of elements in a sequence.
---@param self Enumerable
---@return integer
function Enumerable:count()

first

-- Returns the first element of a sequence.
---@param self Enumerable
---@param predicate? fun(item: any): boolean
---@return any
function Enumerable:first(predicate)

firstOrDefault

-- Returns the first element of a sequence, or a default value if no element is found.
---@param self Enumerable
---@param predicate? fun(item: any): boolean
---@param defaultItem? any
---@return any
function Enumerable:firstOrDefault(predicate, defaultItem)

last

-- Returns the last element of a sequence.
---@param self Enumerable
---@return any
function Enumerable:last()

lastOrDefault

-- Returns the last element of a sequence, or a default value if no element is found.
---@param self Enumerable
---@param defaultItem? any
---@return any
function Enumerable:lastOrDefault(defaultItem)

max

-- Returns the maximum value in a sequence of values.
---@param self Enumerable
---@return number
function Enumerable:max()

min

-- Returns the minimum value in a sequence of values.
---@param self Enumerable
---@return number
function Enumerable:min()

sum

-- Returns the sum value of a sequence of values.
function Enumerable:sum()

concat

-- Adds elements from one sequence to the other.
---@param self Enumerable
---@param other Enumerable
function Enumerable:concat(other)

difference

-- Gets the difference between two sequences
---@param self Enumerable
---@param other Enumerable
function Enumerable:difference(other)

differenceBy

-- Gets the difference between two sequences based on a key
---@param self Enumerable
---@param keySelector? fun(value: any, index: any): any
function Enumerable:differenceBy(other, keySelector)

flatMap

-- Maps items from one sequence to another.
---@param self Enumerable
---@param selector fun(value: any, index: integer): any
function Enumerable:flatMap(selector)

groupBy

--Groups the elements of a sequence.
---@param self Enumerable
---@param keySelector fun(param: any): any
---@param elementSelector? fun(param: any): any
function Enumerable:groupBy(keySelector, elementSelector)

groupByResult

--Groups the elements of a sequence according to a specified key selector function and creates a result value from each group and its key. The elements of each group are projected by using a specified function.
---@param self Enumerable
---@param keySelector fun(param: any): any
---@param elementSelector? fun(param: any): any
---@param resultSelector fun(key: any, grouping: Grouping): any
function Enumerable:groupByResult(keySelector, elementSelector, resultSelector)

intersect

--Produces the set intersection of two sequences.
---@param self Enumerable
---@param other Enumerable
function Enumerable:intersect(other)

intersectBy

--Produces the set intersection of two sequences according to a specified key selector function.
---@param self Enumerable
---@param other Enumerable
---@param keySelector? fun(value: any, index: any): any
function Enumerable:intersectBy(other, keySelector)

map

--Projects each element of a sequence into a new form.
---@param self Enumerable
---@param selector fun(value: any, index: any): any
function Enumerable:map(selector)

sort

--Sorts a sequence.
---@param self Enumerable
---@return SortableIterator
function Enumerable:sort()

sortDescending

--Sorts a sequence in descending order.
---@param self Enumerable
---@return SortableIterator
function Enumerable:sortDescending()

sortBy

--Sorts a sequence based on a specified key selector function.
---@param self Enumerable
---@return SortableIterator
function Enumerable:sortBy(keySelector)

sortByDescending

--Sorts a sequence based on a specified key selector function in descending order.
---@param self Enumerable
---@return SortableIterator
function Enumerable:sortByDescending(keySelector)

thenSortBy

--Sorts a sorted sequence further based on a specified key selector function.
---@param self SortableIterator
---@return SortableIterator
function Enumerable:thenSortBy(keySelector)

thenSortByDescending

--Sorts a sorted sequence further based on a specified key selector function in descending order.
---@param self SortableIterator
---@return SortableIterator
function Enumerable:thenSortByDescending(keySelector)

union

--Produces the set union of two sequences according to a specified key selector function.
---@param self Enumerable
---@param other any
function Enumerable:union(other)

unionBy

--Produces the set union of two sequences according to a specified key selector function.
---@param self Enumerable
---@param other any
---@param keySelector? fun(value: any, index: any): any
function Enumerable:unionBy(other, keySelector)

unique

--Returns unique (distinct) elements from a sequence according to a specified key selector function. 
---@param self Enumerable
function Enumerable:unique()

uniqueBy

--Returns unique (distinct) elements from a sequence according to a specified key selector function. 
---@param self Enumerable
---@param keySelector? fun(value: any, index: any): any
function Enumerable:uniqueBy(keySelector)

where

--Filters a sequence of values based on a predicate.
---@param self Enumerable
---@param predicate fun(value: any, index: any): boolean
function Enumerable:where(predicate)

zip

--Projects each element of a sequence to an Enumerable and flattens the resulting sequences into one sequence.
---@param self Enumerable
---@param other any
function Enumerable:zip(other)

zipBy

--Projects each element of a sequence to an Enumerable and flattens the resulting sequences into one sequence.
---@param self Enumerable
---@param other any
---@param resultSelector? fun(left: any, right: any): any
function Enumerable:zipBy(other, resultSelector)

toDictionary

---@param self Enumerable
---@param keySelector fun(item: any, index: any): any
---@param valueSelector fun(item: any, index: any): any
---@return table
function Enumerable:toDictionary(keySelector, valueSelector)

toTable

---@param self Enumerable
---@return table
function Enumerable:toTable()

toHashSet

---@param self Enumerable
---@return table
function Enumerable:toHashSet()

local Enumerable = require('Module:Enumerable')
local Iterators = require('Module:Iterators')

-- Helper function to check if objects are equal.
local function 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

-- Determines whether all elements of a sequence satisfy a condition.
---@param self Enumerable
---@param predicate fun(item: any): boolean
---@return boolean
function Enumerable:all(predicate)
	assert(predicate)
    local enum = self:getEnumerator()
    while enum:moveNext() == true do
        if predicate(enum.current) == false then
            return false
        end
    end

    return true
end

-- Determines whether any element of a sequence exists or satisfies a condition.
---@param self Enumerable
---@param predicate fun(item: any): boolean
---@return boolean
function Enumerable:any(predicate)
	assert(predicate)
    local enum = self:getEnumerator()
	if predicate then
		while enum:moveNext() == true do
			if predicate(enum.current) == true then
				return true
			end
		end
	else
		while enum:moveNext() == true do
			return true
		end
	end
	return false
end

-- Adds an item to the end of the enumerable
---@param self Enumerable
---@param item any
function Enumerable:append(item)
	return Iterators.AppendIterator.new(self, item, nil, true)
end

-- Adds an item to the front of the enumerable
---@param self Enumerable
---@param item any
function Enumerable:prepend(item)
	return Iterators.AppendIterator.new(self, item, nil, false)
end

-- Determines whether a sequence contains a specified element.
---@param self Enumerable
---@param item any
---@return boolean
function Enumerable:contains(item)
	assert(item)
	local enum = self:getEnumerator()
	while enum:moveNext() == true do
		if isEqual(enum.current, item) == true then
			return true
		end
	end
	return false
end

-- Returns the number of elements in a sequence.
---@param self Enumerable
---@return integer
function Enumerable:count()
	if self.getCount ~= nil then
		return self:getCount()
	end

	local count = 0
	local enum = self:getEnumerator()
	while enum:moveNext() do
		count = count + 1
	end
	return count
end

-- Returns the first element of a sequence.
---@param self Enumerable
---@param predicate? fun(item: any): boolean
---@return any
function Enumerable:first(predicate)
	local result = Enumerable.firstOrDefault(self, predicate, nil)
	if result ~= nil then
		return result
	end
	error('No items in sequence.')
end

-- Returns the first element of a sequence, or a default value if no element is found.
---@param self Enumerable
---@param predicate? fun(item: any): boolean
---@param defaultItem? any
---@return any
function Enumerable:firstOrDefault(predicate, defaultItem)
	assert(predicate)
	local enum = self:getEnumerator()
	if predicate then
		while enum:moveNext() do
			if predicate(enum.current) == true then
				return enum.current
			end
		end
	else
		while enum:moveNext() do
			return enum.current
		end
	end
	return defaultItem
end

-- Returns the last element of a sequence.
---@param self Enumerable
---@return any
function Enumerable:last()
	local result = Enumerable.lastOrDefault(self, nil)
	if result ~= nil then
		return result
	end
	error('Sequence contains no items.')
end

-- Returns the last element of a sequence, or a default value if no element is found.
---@param self Enumerable
---@param defaultItem? any
---@return any
function Enumerable:lastOrDefault(defaultItem)
	local enum = self:getEnumerator()
	local last = nil
	if self.getLast ~= nil then
		last = self:getLast()
	else
		-- Run enumerator to the end.
		while enum:moveNext() == true do end
		last = enum.current
	end

	return last
end

-- Returns the maximum value in a sequence of values.
---@param self Enumerable
---@return number
function Enumerable:max()
	local h = Enumerable.first(self)
	local enum = self:getEnumerator()
	while enum:moveNext() do
		local num = tonumber(enum.current)
		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.
---@param self Enumerable
---@return number
function Enumerable:min()
	local l = Enumerable.first(self)
	local enum = self:getEnumerator()
	while enum:moveNext() do
		local num = tonumber(enum.current)
		if num == nil then
			error('Value is NaN')
		end
		if num < l then l = num end
	end

	return l
end

function Enumerable:sum()
	local total = 0
	local enum = self:getEnumerator()
	while enum:moveNext() do
		local num = tonumber(enum.current)
		if num == nil then
			error('Value is NaN')
		end
		total = total + num
	end
	return total
end

-- Adds elements from one sequence to the other.
---@param self Enumerable
---@param other Enumerable
function Enumerable:concat(other)
	return Iterators.ConcatIterator.new(self, other)
end

-- Gets the difference between two sequences
---@param self Enumerable
---@param other Enumerable
function Enumerable:difference(other)
	return Iterators.DifferenceIterator.new(self, other)
end

-- Gets the difference between two sequences based on a key
---@param self Enumerable
---@param keySelector? fun(value: any, index: any): any
function Enumerable:differenceBy(other, keySelector)
	return Iterators.DifferenceIterator.new(self, other, keySelector)
end

-- Maps items from one sequence to another.
---@param self Enumerable
---@param selector fun(value: any, index: integer): any
function Enumerable:flatMap(selector)
	return Iterators.FlatMapIterator.new(self, selector)
end

---@param self Enumerable
---@param keySelector fun(param: any): any
---@param elementSelector? fun(param: any): any
function Enumerable:groupBy(keySelector, elementSelector)
	return Iterators.GroupByIterator.new(self, keySelector, elementSelector)
end

---@param self Enumerable
---@param keySelector fun(param: any): any
---@param elementSelector? fun(param: any): any
---@param resultSelector fun(key: any, grouping: Grouping): any
function Enumerable:groupByResult(keySelector, elementSelector, resultSelector)
	return Iterators.GroupByResultIterator.new(self, keySelector, elementSelector, resultSelector)
end

---@param self Enumerable
---@param other Enumerable
function Enumerable:intersect(other)
	return Iterators.IntersectIterator.new(self, other)
end

---@param self Enumerable
---@param other Enumerable
---@param keySelector? fun(value: any, index: any): any
function Enumerable:intersectBy(other, keySelector)
	return Iterators.IntersectIterator.new(self, other, keySelector)
end

---@param self Enumerable
---@param selector fun(value: any, index: any): any
function Enumerable:map(selector)
	return Iterators.MapIterator.new(self, selector)
end

---@param self Enumerable
---@return SortableIterator
function Enumerable:sort()
	return Iterators.SortableIterator.new(self, function(x) return x end, false)
end

---@param self Enumerable
---@return SortableIterator
function Enumerable:sortDescending()
	return Iterators.SortableIterator.new(self, function(x) return x end, true)
end

---@param self Enumerable
---@return SortableIterator
function Enumerable:sortBy(keySelector)
	return Iterators.SortableIterator.new(self, keySelector, false)
end

---@param self Enumerable
---@return SortableIterator
function Enumerable:sortByDescending(keySelector)
	return Iterators.SortableIterator.new(self, keySelector, true)
end

---@param self SortableIterator
---@return SortableIterator
function Enumerable:thenSortBy(keySelector)
	assert(self.createSortableIterator, 'Previous operation must be a Sort or SortBy')
	return self:createSortableIterator(keySelector, false)
end

---@param self SortableIterator
---@return SortableIterator
function Enumerable:thenSortByDescending(keySelector)
	assert(self.createSortableIterator, 'Previous operation must be a Sort or SortBy')
	return self:createSortableIterator(keySelector, true)
end

---@param self Enumerable
---@param other any
function Enumerable:union(other)
	return Iterators.UnionIterator.new(self, other)
end

---@param self Enumerable
---@param other any
---@param keySelector? fun(value: any, index: any): any
function Enumerable:unionBy(other, keySelector)
	return Iterators.UnionIterator.new(self, other, keySelector)
end

---@param self Enumerable
function Enumerable:unique()
	return Iterators.UniqueIterator.new(self)
end

---@param self Enumerable
---@param keySelector? fun(value: any, index: any): any
function Enumerable:uniqueBy(keySelector)
	return Iterators.UniqueIterator.new(self, keySelector)
end

---@param self Enumerable
---@param predicate fun(value: any, index: any): boolean
function Enumerable:where(predicate)
	return Iterators.WhereIterator.new(self, predicate)
end

---@param self Enumerable
---@param other any
function Enumerable:zip(other)
	return Iterators.ZipIterator.new(self, other)
end

---@param self Enumerable
---@param other any
---@param resultSelector? fun(left: any, right: any): any
function Enumerable:zipBy(other, resultSelector)
	return Iterators.ZipIterator.new(self, other, resultSelector)
end

---@param self Enumerable
---@param keySelector fun(item: any, index: any): any
---@param valueSelector fun(item: any, index: any): any
---@return table
function Enumerable:toDictionary(keySelector, valueSelector)
	assert(keySelector)
	assert(valueSelector)
	local tbl = {}
	local enum = self:getEnumerator()
	while enum:moveNext() == true do
		local index = enum.index
		local current = enum.current

		local key = keySelector(current, index)
		local value = valueSelector(current, index)
		tbl[key] = value
	end
	return tbl
end

---@param self Enumerable
---@return table
function Enumerable:toTable()
	if self.getTable ~= nil then
		return self:getTable()
	end

	local tbl = {}
	local enum = self:getEnumerator()
	while enum:moveNext() do
		table.insert(tbl, enum.current)
	end
	return tbl
end

---@param self Enumerable
---@param keySelector fun(item: any, index: any): any
---@return table
function Enumerable:toHashSet(keySelector)
	local set = {}
	local enum = self:getEnumerator()
	while enum:moveNext() == true do
		local key = keySelector(enum.current, enum.index)
		set[key] = true
	end
	return set
end