314 lines
5.6 KiB
Lua
314 lines
5.6 KiB
Lua
--This is a copy from LuaChat OrderedMap, should keep in consistent with the one in LuaChat.
|
|
--TODO: Use the Immutable instead after it embeds the OrderedMap
|
|
local OrderedMap = {}
|
|
|
|
OrderedMap.__index = OrderedMap
|
|
|
|
--[[
|
|
Create a new OrderedMap with the given ID function and sort predicates.
|
|
|
|
getId might look like:
|
|
|
|
function(object)
|
|
return object.id
|
|
end
|
|
|
|
And the sortPredicate might look like:
|
|
|
|
function(objectA, objectB)
|
|
return objectA.id < objectB.id
|
|
end
|
|
|
|
The rest of the arguments are inserted into the OrderedMap as values.
|
|
]]
|
|
function OrderedMap.new(getId, sortPredicate, ...)
|
|
local self = {
|
|
keys = {},
|
|
values = {},
|
|
getId = getId,
|
|
sortPredicate = sortPredicate
|
|
}
|
|
|
|
setmetatable(self, OrderedMap)
|
|
|
|
OrderedMap._InsertInPlace(self, ...)
|
|
|
|
return self
|
|
end
|
|
|
|
--[[
|
|
Gets the value in the map associated with the given ID.
|
|
]]
|
|
function OrderedMap:Get(id)
|
|
return self.values[id]
|
|
end
|
|
|
|
--[[
|
|
Gets the value in the map located at the given index.
|
|
]]
|
|
function OrderedMap:GetByIndex(index)
|
|
local id = self.keys[index]
|
|
|
|
if id == nil then
|
|
return nil
|
|
end
|
|
|
|
return self.values[id]
|
|
end
|
|
|
|
--[[
|
|
Gets the list of IDs that are present in the OrderedMap.
|
|
]]
|
|
function OrderedMap:GetIds()
|
|
return self.keys
|
|
end
|
|
|
|
--[[
|
|
Returns the number of elements in the OrderedMap.
|
|
]]
|
|
function OrderedMap:Length()
|
|
return #self.keys
|
|
end
|
|
|
|
--[[
|
|
Deletes the given keys from the map.
|
|
|
|
This is an immutable operation, so the original map is not modified.
|
|
|
|
Example:
|
|
|
|
map = map:Delete("my-key", "another-key")
|
|
]]
|
|
function OrderedMap:Delete(...)
|
|
local new = OrderedMap.new(self.getId, self.sortPredicate)
|
|
|
|
local len = select("#", ...)
|
|
|
|
for key, value in pairs(self.values) do
|
|
new.values[key] = value
|
|
end
|
|
|
|
for i = 1, len do
|
|
local key = select(i, ...)
|
|
|
|
new.values[key] = nil
|
|
end
|
|
|
|
for _, value in ipairs(self.keys) do
|
|
if new.values[value] ~= nil then
|
|
new.keys[#(new.keys)+1] = value
|
|
end
|
|
end
|
|
|
|
return new
|
|
end
|
|
|
|
--[[
|
|
Inserts the given values into the map.
|
|
|
|
This is an immutable operation, so the original map is not modified and a
|
|
new map is returned instead.
|
|
|
|
Example:
|
|
|
|
map = map:Insert("Hello", "World")
|
|
]]
|
|
function OrderedMap:Insert(...)
|
|
local new = self:_Copy()
|
|
|
|
OrderedMap._InsertInPlace(new, ...)
|
|
|
|
return new
|
|
end
|
|
|
|
--[[
|
|
Returns the first value in the OrderedMap.
|
|
]]
|
|
function OrderedMap:First()
|
|
if self.keys[1] then
|
|
return self:Get(self.keys[1])
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Returns the last value in the OrderedMap.
|
|
]]
|
|
function OrderedMap:Last()
|
|
local i = #self.keys
|
|
if self.keys[i] then
|
|
return self:Get(self.keys[i])
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Create an iterator to traverse the map front-to-back.
|
|
|
|
Example:
|
|
|
|
for id, item in map:CreateIterator() do
|
|
print(id, item)
|
|
end
|
|
]]
|
|
function OrderedMap:CreateIterator()
|
|
local i = 0
|
|
local length = #self.keys
|
|
|
|
-- Iterator function
|
|
return function()
|
|
i = i + 1
|
|
if i <= length then
|
|
local key = self.keys[i]
|
|
return key, self.values[key], i
|
|
end
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Create an iterator to traverse the map back-to-front.
|
|
|
|
Example:
|
|
|
|
for id, item in map:CreateReverseIterator() do
|
|
print(id, item)
|
|
end
|
|
]]
|
|
function OrderedMap:CreateReverseIterator()
|
|
local i = #self.keys + 1
|
|
|
|
-- Iterator function
|
|
return function()
|
|
i = i - 1
|
|
if i > 0 then
|
|
local key = self.keys[i]
|
|
return key, self.values[key], i
|
|
end
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Create a new OrderedMap, applying the given predicate to each element in
|
|
this OrderedMap.
|
|
|
|
Analogous to 'map' on a list.
|
|
|
|
Example:
|
|
|
|
local doubled = map:Map(function(value, key)
|
|
return value * 2
|
|
end)
|
|
]]
|
|
function OrderedMap:Map(predicate)
|
|
local new = OrderedMap.new(self.getId, self.sortPredicate)
|
|
|
|
for key, value in self:CreateIterator() do
|
|
new:_InsertInPlaceUnsorted(predicate(value, key))
|
|
end
|
|
|
|
new:_Sort()
|
|
|
|
return new
|
|
end
|
|
|
|
--[[
|
|
Merges two or more OrderedMap objects by combining their values.
|
|
|
|
Values from the right-most arguments will overwrite values from the left.
|
|
|
|
Example:
|
|
|
|
local merged = OrderedMap.Merge(a, b, c)
|
|
|
|
OR
|
|
|
|
local merged = a:Merge(b, c)
|
|
|
|
NOTE: This function is not guaranteed to create a new OrderedMap.
|
|
]]
|
|
function OrderedMap:Merge(...)
|
|
local new
|
|
|
|
for i = 1, select("#", ...) do
|
|
local other = select(i, ...)
|
|
|
|
if other:Length() > 0 then
|
|
if not new then
|
|
new = self:_Copy()
|
|
end
|
|
|
|
for _, value in other:CreateIterator() do
|
|
new:_InsertInPlaceUnsorted(value)
|
|
end
|
|
end
|
|
end
|
|
|
|
if new then
|
|
new:_Sort()
|
|
|
|
return new
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--[[
|
|
Internal method for creating a copy of this OrderedMap.
|
|
]]
|
|
function OrderedMap:_Copy()
|
|
local new = OrderedMap.new(self.getId, self.sortPredicate)
|
|
|
|
for key, value in ipairs(self.keys) do
|
|
new.keys[key] = value
|
|
end
|
|
|
|
for key, value in pairs(self.values) do
|
|
new.values[key] = value
|
|
end
|
|
|
|
return new
|
|
end
|
|
|
|
--[[
|
|
Internal method for inserting a value without sorting the map.
|
|
|
|
This means that the invariants that the map exposes will be broken until
|
|
the _Sort() method is called.
|
|
]]
|
|
function OrderedMap:_InsertInPlaceUnsorted(...)
|
|
local len = select("#", ...)
|
|
|
|
for i = 1, len do
|
|
local item = select(i, ...)
|
|
local key = self.getId(item)
|
|
|
|
if not self.values[key] then
|
|
table.insert(self.keys, key)
|
|
end
|
|
|
|
self.values[key] = item
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Sorts the map; used in cases where the map would become out-of-order when
|
|
using internal recommendations.
|
|
]]
|
|
function OrderedMap:_Sort()
|
|
table.sort(self.keys, function(keyA, keyB)
|
|
local a = self.values[keyA]
|
|
local b = self.values[keyB]
|
|
|
|
return self.sortPredicate(a, b)
|
|
end)
|
|
end
|
|
|
|
--[[
|
|
Inserts the given values into the map in-place.
|
|
|
|
This operation mutates the map; generally you should use Insert instead.
|
|
]]
|
|
function OrderedMap:_InsertInPlace(...)
|
|
self:_InsertInPlaceUnsorted(...)
|
|
self:_Sort()
|
|
end
|
|
|
|
return OrderedMap |