339 lines
8.8 KiB
Lua
339 lines
8.8 KiB
Lua
--- Unique Array class
|
|
-- For working with unique string array values. Creates an array with hash/dictionary indexing.
|
|
-- @classmod unique_array
|
|
-- Adding or removeing values without using the provided methods can break the functionality of this class.
|
|
-- Additional methods exported by requering unique_array are .set and .make_dictionary
|
|
-- @usage local Unique_Array = require('__stdlib__/stdlib/utils/classes/unique_array')
|
|
-- local my_array = Unique_Array('first')
|
|
-- my_array:add('second')
|
|
-- if my_array['second'] then
|
|
-- print('its truthy')
|
|
-- end'
|
|
-- @set all=true
|
|
|
|
local M = {
|
|
__class = 'unique_array'
|
|
}
|
|
|
|
local type, ipairs = type, ipairs
|
|
local getmetatable, setmetatable, rawset = getmetatable, setmetatable, rawset
|
|
local t_remove, t_sort = table.remove, table.sort
|
|
|
|
local function add(self, class, param)
|
|
local index = #self + 1
|
|
rawset(self, index, param)
|
|
class.dictionary[param] = index
|
|
class.len = class.len + 1
|
|
return index
|
|
end
|
|
|
|
local function dictionary_sort(self, class)
|
|
class.len = 0
|
|
for i, v in ipairs(self) do
|
|
class.len = class.len + 1
|
|
class.dictionary[v] = i
|
|
end
|
|
end
|
|
|
|
local function remove(self, class, param)
|
|
t_remove(self, class.dictionary[param])
|
|
class.dictionary[param] = nil
|
|
dictionary_sort(self, class)
|
|
end
|
|
|
|
local wrappers = {}
|
|
|
|
local function create_class(tab)
|
|
if type(tab) == 'table' then
|
|
local class = {
|
|
__concat = M.concat,
|
|
__tostring = M.tostring,
|
|
__eq = M.same,
|
|
__lt = wrappers.__lt,
|
|
__add = wrappers.__add,
|
|
__sub = wrappers.__sub,
|
|
__lte = wrappers.__lt,
|
|
len = 0
|
|
}
|
|
|
|
class.dictionary = M.make_dictionary(tab)
|
|
|
|
class.__index = function(_, k)
|
|
return class.dictionary[k] or M[k]
|
|
end
|
|
|
|
return setmetatable(tab, class)
|
|
end
|
|
end
|
|
|
|
local function unique_or_new(tab)
|
|
if type(tab) == table and tab.__class == 'unique_array' then
|
|
return tab
|
|
else
|
|
return M.new(tab)
|
|
end
|
|
end
|
|
|
|
wrappers.__lt = function(lhs, rhs)
|
|
lhs, rhs = unique_or_new(lhs), unique_or_new(rhs)
|
|
return rhs:all(lhs)
|
|
end
|
|
wrappers.__add = function(lhs, rhs)
|
|
return M.new(lhs):add(rhs)
|
|
end
|
|
wrappers.__sub = function(lhs, rhs)
|
|
return M.new(lhs):remove(rhs)
|
|
end
|
|
wrappers.__pairs = function(self)
|
|
local dictionary = getmetatable(self).dictionary
|
|
return next, dictionary, nil
|
|
end
|
|
|
|
--- Methods
|
|
-- @section Methods
|
|
|
|
--- Create a new unique array.
|
|
-- @tparam unique_array|string|{string,...} ... strings to initialize the unique array with
|
|
-- @treturn @{unique_array} new
|
|
function M.new(...)
|
|
return create_class {}:add(type((...)) == 'table' and (...) or { ... })
|
|
end
|
|
|
|
--- Add a string to the array if it doesn't exist in the array.
|
|
-- @tparam unique_array|string|{string,...} other
|
|
-- @treturn @{unique_array} self
|
|
function M:add(other)
|
|
local class = getmetatable(self)
|
|
for _, param in ipairs(type(other) == 'table' and other or { other }) do
|
|
if not self[param] then
|
|
add(self, class, param)
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Remove the strings from the array if they exist.
|
|
-- @tparam unique_array|string|{string,...} other
|
|
-- @treturn @{unique_array} self
|
|
function M:remove(other)
|
|
local class = getmetatable(self)
|
|
for _, param in ipairs(type(other) == 'table' and other or { other }) do
|
|
if self[param] then
|
|
remove(self, class, param)
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Toggles the passed name in the array by adding it if not present or removing it if it is.
|
|
-- @tparam unique_array|string|{string,...} other
|
|
-- @treturn @{unique_array} self
|
|
function M:toggle(other)
|
|
local class = getmetatable(self)
|
|
for _, param in ipairs(type(other) == 'table' and other or { other }) do
|
|
if self[param] then
|
|
remove(self, class, param)
|
|
else
|
|
add(self, class, param)
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Get all items that are NOT in both arrays.
|
|
-- @tparam unique_array|string|{string,...} other
|
|
-- @treturn @{unique_array} new
|
|
function M:diff(other)
|
|
other = unique_or_new(other)
|
|
local diff = M.new()
|
|
for _, v in ipairs(self) do
|
|
if not other[v] then
|
|
diff:add(v)
|
|
end
|
|
end
|
|
for _, v in ipairs(other) do
|
|
if not self[v] then
|
|
diff:add(v)
|
|
end
|
|
end
|
|
return diff
|
|
end
|
|
|
|
--- Get all items that are in both arrays.
|
|
-- @tparam unique_array|string|{string,...} other
|
|
-- @treturn @{unique_array} new
|
|
function M:intersects(other)
|
|
other = unique_or_new(other)
|
|
local intersection = M.new()
|
|
for _, v in ipairs(self) do
|
|
if other[v] then
|
|
intersection:add(v)
|
|
end
|
|
end
|
|
for _, v in ipairs(other) do
|
|
if self[v] and not intersection[v] then
|
|
intersection:add(v)
|
|
end
|
|
end
|
|
return intersection
|
|
end
|
|
|
|
--- Sort the unique_array in place.
|
|
-- @tparam[opt] function cmp Comparator @{sort} function to use
|
|
-- @treturn @{unique_array} self
|
|
function M:sort(cmp)
|
|
local class = getmetatable(self)
|
|
t_sort(self, cmp)
|
|
dictionary_sort(self, class)
|
|
return self
|
|
end
|
|
|
|
--- Create a new unique_array by concatenating together.
|
|
-- @tparam unique_array|string|{string,...} other
|
|
-- @treturn @{unique_array} new
|
|
function M:concat(other)
|
|
return M.new(self):add(other)
|
|
end
|
|
|
|
--- Find all members in a unique array that match the pattern.
|
|
-- @tparam string pattern Lua @{pattern}
|
|
-- @treturn @{unique_array} new unique array containing all elements that match.
|
|
function M:find(pattern)
|
|
local matches = M.new()
|
|
for _, value in ipairs(self) do
|
|
if value:find(pattern) then
|
|
matches:add(value)
|
|
end
|
|
end
|
|
return matches
|
|
end
|
|
|
|
--- Clear the array returning an empty array object
|
|
-- @treturn @{unique_array} self
|
|
function M:clear()
|
|
local class = getmetatable(self)
|
|
for i = #self, 1, -1 do
|
|
self[i] = nil
|
|
end
|
|
class.dictionary = {}
|
|
class.len = 0
|
|
return self
|
|
end
|
|
|
|
--- Functions
|
|
-- @section Functions
|
|
|
|
--- Does this array have all of the passed strings.
|
|
-- @tparam unique_array|string|{string,...} other
|
|
-- @treturn boolean every passed string is in the array
|
|
function M:all(other)
|
|
local params = type(other) == 'table' and other or { other }
|
|
local count = 0
|
|
for _, param in ipairs(params) do
|
|
if self[param] then
|
|
count = count + 1
|
|
end
|
|
end
|
|
return count == #params
|
|
end
|
|
M.has = M.all
|
|
|
|
--- Does this array have any of the passed strings.
|
|
-- @tparam unique_array|string|{string,...} other
|
|
-- @treturn boolean any passed string is in the array
|
|
function M:any(other)
|
|
for _, param in ipairs(type(other) == 'table' and other or { other }) do
|
|
if self[param] then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
--- Does this array have none of the passed strings.
|
|
-- @tparam unique_array|string|{string,...} other
|
|
-- @treturn boolean no passed strings are in the array
|
|
function M:none(other)
|
|
for _, param in ipairs(type(other) == 'table' and other or { other }) do
|
|
if self[param] then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
--- Do both arrays contain the same items
|
|
-- @tparam unique_array|string|{string,...} other
|
|
-- @treturn boolean
|
|
function M:same(other)
|
|
other = unique_or_new(other)
|
|
if #self == #other then
|
|
for _, value in ipairs(self) do
|
|
if not other[value] then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
--- Do the unique arrays have no items in common
|
|
-- @tparam unique_array|string|{string,...} other
|
|
-- @treturn boolean
|
|
function M:disjointed(other)
|
|
return #self:intersects(other) == 0
|
|
end
|
|
|
|
--- Convert the array to a string.
|
|
-- @treturn string
|
|
function M:tostring()
|
|
return table.concat(self, ', ')
|
|
end
|
|
|
|
--- Return a dictionary mapping of the array.
|
|
-- can be passed a function to set the value of the field.
|
|
-- @tparam[opt] function func value, index are passed as the first two paramaters
|
|
-- @tparam[opt] any ... additional values to pass to the function
|
|
-- @treturn dictionary
|
|
function M:make_dictionary(func, ...)
|
|
local dict = {}
|
|
for index, value in ipairs(self) do
|
|
dict[value] = func and func(value, index, ...) or index
|
|
end
|
|
return dict
|
|
end
|
|
|
|
--- Exports
|
|
-- @section Exports
|
|
|
|
local function from_dictionary(dict)
|
|
local array = {}
|
|
local i = 0
|
|
for k in pairs(dict) do
|
|
i = i + 1
|
|
array[i] = k
|
|
end
|
|
return create_class(array)
|
|
end
|
|
|
|
--- These functions are available when requiring this class.
|
|
-- @table exports
|
|
local exports = {
|
|
new = M.new, -- @{unique_array.new}
|
|
set = create_class, -- set the class on an existing table
|
|
dictionary = M.make_dictionary, -- @{unique_array.make_dictionary}
|
|
from_dictionary = from_dictionary
|
|
}
|
|
|
|
setmetatable(
|
|
exports,
|
|
{
|
|
__call = function(_, ...)
|
|
return M.new(...)
|
|
end
|
|
}
|
|
)
|
|
|
|
return exports
|