Hello everyone! I am making a module where I add extra functions to many default libraries (like math, string, pairs, ipairs, etc…). The code you are going to see is some extra functions I belive could be useful for the table library, I am posting it here to see if there is anything I can optimize or any point I need to make more clear with comments. Thanks a bunch!
A comment before seeing the code is that there is a function called AddRemoveSelf
and RemoveSelf
, it is used by my module to allow people to call the function however they want, example: table.delete
and table:delete
Utilities:AddRemoveSelf(module);
-- Checks if a table is empty.
-- ¿Why use this function?
-- Well, to check if an array is empty is easy, just do 0 == #array. This function, however, also checks
-- dictionaries.
module.isempty = function(...)
local arguments = module:RemoveSelf(...);
local t1 = arguments[1];
assert(type(t1) == "table", "Error at table(isempty). Expected positional argument 1 to be a table\nGot '" .. typeof(t1) .. "' instead.");
return module.length(t1) == 0;
end
-- Checks if a table is an array.
module.isarray = function(...)
local arguments = module:RemoveSelf(...);
local t1 = arguments[1];
assert(type(t1) == "table", "Error at table(isarray). Expected positional argument 1 to be a table\nGot '" .. typeof(t1) .. "' instead.");
return module.length(t1) == #t1;
end
-- Merges two dictionaries together.
module.merge = function(...)
local arguments = module:RemoveSelf(...);
local t1 = arguments[1];
assert(type(t1) == "table", "Error at table(merge). Expected positional argument 1 to be a table\nGot '" .. typeof(t1) .. "' instead.");
table.remove(arguments, 1);
for i, t in ipairs(arguments) do
assert(type(t) == "table", "Error at table(merge). value at index '" .. tostring(i+1) .. "' is not a table\nExpected all values to be tables\nGot '" .. typeof(t) .. "' instead.");
for k, v in pairs(t) do
t1[k] = v;
end
end
return t1;
end
-- Merges two dictionaries or arrays together. This, however, makes all keys indexes.
-- Example:
-- {
-- 1: "hello"
-- 2: "nice"
-- 3: "cool"
-- }
-- {
-- 1: "a"
-- 2: "b"
-- 3: "c"
-- }
-- Result:
-- {
-- 1: "hello"
-- 2: "nice"
-- 3: "cool"
-- 4: "a"
-- 5: "b"
-- 6: "c"
-- }
module.imerge = function(...)
local arguments = module:RemoveSelf(...);
local t1 = arguments[1];
assert(type(t1) == "table", "Error at table(imerge). Expected positional argument 1 to be a table\nGot '" .. typeof(t1) .. "' instead.");
table.remove(arguments, 1);
for i, t in ipairs(arguments) do
assert(type(t) == "table", "Error at table(imerge). Expected all indexes of position argument 1 to be tables\nGot '" .. typeof(t) .. "' on index '" .. tostring(i) .. "' instead.");
local f = pairs;
if(module.isarray(t)) then f = ipairs end; --We need to use ipairs on an array to ensure the
-- indexes are organized
for _, v in f(t) do
table.insert(t1, v)
end
end
return t1;
end
-- Replacement for table.remove, adds support to proxies.
module.remove = function(...)
local arguments = module:RemoveSelf(...);
local t, index, skip = arguments[1], arguments[2], arguments[3];
assert(type(t) == "table", "Error at table(remove). Expected positional argument 1 to be a table\nGot '" .. typeof(t) .. "' instead.");
assert(type(index) == "number", "Error at table(remove). Expected positional argument 2 to be a number\nGot '" .. typeof(index) .. "' instead.");
local metatable = getmetatable(t);
if(not skip and metatable and type(metatable.controller) == "table" and type(metatable.controller.remove) == "function") then
return metatable.controller.remove(t,index);
else return table.remove(t, index) end;
end
-- Replacement for table.insert, adds support to proxies.
module.insert = function(...)
local arguments = module:RemoveSelf(...);
local t, index, value, skip = arguments[1], arguments[2], arguments[3], arguments[4];
assert(type(t) == "table", "Error at table(insert). Expected positional argument 1 to be a table\nGot '" .. typeof(t) .. "' instead.");
assert(type(index) ~= "nil" or type(value) ~= "nil", "Error at table(insert). Expected positional argument 2\nGot '" .. typeof(index) .. "' instead.");
-- Re-organizes the arguments if some of them are missing
arguments[4] = nil;
if(not index) then
table.remove(arguments, 2);
end
if(type(value) == "nil") then
value = index;
index = nil;
end
--
local metatable = getmetatable(t);
if(not skip and metatable and type(metatable.controller) == "table" and type(metatable.controller.insert) == "function") then
return metatable.controller.insert(t,index, value);
else return table.insert(table.unpack(arguments)) end;
end
-- Removes all the duplicate values in a table, leaving only one of each
module.unique = function(...)
local arguments = module:RemoveSelf(...);
local t1 = arguments[1];
assert(type(t1) == "table", "Error at unique(collapse). Expected positional argument 1 to be a table\nGot '" .. typeof(t1) .. "' instead.");
local clone = {};
if(module.isarray(t1)) then
local found = {};
for _, v in ipairs(t1) do
if(not table.find(clone, v)) then
table.insert(clone, v);
end
end
else
local found = {};
for k, v in pairs(t1) do
if(not table.find(found, v)) then
table.insert(found, v);
clone[k] = v;
end
end
end
return clone;
end
-- Gets the real length of a table. For example, trying to get the length of a dictionary will not return
-- it's real length and will instead return 0.
module.length = function(...)
local arguments = module:RemoveSelf(...);
local t = arguments[1];
assert(type(t) == "table", "Error at table(length). Expected positional argument 1 to be a table\nGot '" .. typeof(t) .. "' instead.");
local realLength = 0;
for k, v in pairs(t) do
realLength = realLength + 1;
end
return realLength;
end
-- Delete the first mention of the given value in an array.
module.delete = function(...)
local arguments = module:RemoveSelf(...);
local t, v, swapRemove = arguments[1], arguments[2], arguments[3];
assert(type(t) == "table", "Error at table(delete). Expected positional argument 1 to be a table\nGot '" .. typeof(t) .. "' instead.");
assert(module.isarray(t), "Error at table(delete). Expected positional argument 1 to be an array.");
local found = false;
for k, v2 in ipairs(t) do
if(v == v2) then
if(swapRemove) then
module.swapremove(t,k);
else module.remove(t, k) end;
found = true;
break
end
end
return found;
end
-- Delete all the mentions of the given value in an array.
module.deleteall = function(...)
local arguments = module:RemoveSelf(...);
local t, v, swapRemove = arguments[1], arguments[2], arguments[3];
assert(type(t) == "table", "Error at table(deleteall). Expected positional argument 1 to be a table\nGot '" .. typeof(t) .. "' instead.");
assert(module.isarray(t), "Error at table(delete). Expected positional argument 1 to be an array.");
local found = 0;
for k, v2 in ipairs(t) do
if(v == v2) then
if(swapRemove) then
module.swapremove(t,k);
else module.remove(t, k) end;
found += 1;
end
end
return found;
end; module.deleteAll = module.deleteall;
-- Re-orders all the values in an array.
module.shuffle = function(...)
local arguments = module:RemoveSelf(...);
local t = arguments[1];
assert(type(t) == "table", "Error at table(shuffle). Expected positional argument 1 to be a table\nGot '" .. typeof(t) .. "' instead.");
for i = #t, 2, -1 do
local j = math.random(i)
t[i], t[j] = t[j], t[i]
end
return t;
end
-- Used by the internals, just an easy way I can add aliases to functions.
-- Examples:
-- MyFunctionName
-- myFunctionName
-- myfunctionname
module.multiindex = function(...)
local arguments = module:RemoveSelf(...);
local t, k, v = arguments[1], arguments[2], arguments[3];
assert(type(t) == "table", "Error at table(multiindex). Expected positional argument 1 to be a table\nGot '" .. typeof(t) .. "' instead.");
assert(type(k) == "string", "Error at table(multiindex). Expected positional argument 2 to be a string\nGot '" .. typeof(k) .. "' instead.");
k = string.upper(string.sub(k,1,1)) .. string.sub(k,2);
t[k] = v;
t[string.lower(string.sub(k,1,1)) .. string.sub(k,2)] = v;
t[string.lower(k)] = v;
return t;
end; module.multiIndex = module.multiindex;
-- Allows for a table to be filtered, where only the allowed values can remain.
-- NOTE: If you use filter on an array it will create gaps, don't worry though since that is why the
-- collapse function in this library exists
module.filter = function(...)
local arguments = module:RemoveSelf(...);
local t, c = arguments[1], arguments[2];
assert(type(t) == "table", "Error at table(filter). Expected positional argument 1 to be a table\nGot '" .. typeof(t) .. "' instead");
local results = {};
local removed = {};
for k,v in pairs(t) do
if(c(t,k,v)) then
table.insert(results, {k,v});
else
table.insert(removed, {k,v});
t[k] = nil;
end
end
return results, removed;
end
-- Same as filter, this however will compare every single value with each other and then filter the
-- results.
module.comparefilter = function(...)
local arguments = module:RemoveSelf(...);
local t, c = arguments[1], arguments[2];
assert(type(t) == "table", "Error at table(comparefilter). Expected positional argument 1 to be a table\nGot '" .. typeof(t) .. "' instead");
assert(type(c) == "function", "Error at table(comparefilter). Expected positional argument 2 to be a function\nGot '" .. typeof(t) .. "' instead");
local clone = table.clone(t);
local results = {};
local removed = {};
for k,v in pairs(clone) do
for _,v2 in pairs(clone) do
if(c(t,v,v2)) then
table.insert(results, {k,v});
else
table.insert(removed, {k,v});
t[k] = nil;
end
end
end
return results, removed;
end; module.compareFilter = module.comparefilter;
-- Swapremove, also called as Fastremove is a method often used for large arrays. When you use
-- table.remove, you move all the keys down by one index, this is a problem for large arrays that do not
-- need to be organized since it takes unnecessary time. To fix this Swapremove replaces the given index
-- with the last index.
module.swapremove = function(...)
local arguments = module:RemoveSelf(module, ...);
local t, i = arguments[1], arguments[2];
assert(type(t) == "table", "Error at table(swapremove). Expected positional argument 1 to be a table\nGot '" .. typeof(t) .. "' instead.");
local size = #t;
if(size ~= i) then t[i] = t[size] end;
t[size] = nil;
return t;
end; module.swapRemove = module.swapremove; module.fastremove = module.fastremove; module.fastRemove = module.fastremove;
-- Transforms every value (or key) in a table to an index, transforming it into an array.
module.enumarate = function(...)
local arguments = module:RemoveSelf(...);
local t, keys = arguments[1], arguments[2];
assert(type(t) == "table", "Error at table(enumarate). Expected positional argument 1 to be a table\nGot '" .. typeof(t) .. "' instead.");
local clone = {};
for i, v in ipairs(t) do
if(not keys) then clone[i] = v;
else clone[i] = i end;
end
for k, v in pairs(t) do
if(not tonumber(k) or tonumber(k) > #t) then
if(not keys) then table.insert(clone, v);
else table.insert(clone, k) end;
end
end
return clone;
end
-- Often times when using functions like 'filter' or 'comparefilter' an array can become hollow (making
-- functions like 'ipairs' not work correctly), to fix this you can collapse an array which will join
-- the empty indexes and remove the holes.
module.collapse = function(...)
local arguments = module:RemoveSelf(...);
local t = arguments[1];
assert(type(t) == "table", "Error at table(collapse). Expected positional argument 1 to be a table\nGot '" .. typeof(t) .. "' instead");
local keys = module.enumarate(t, true);
local numbers = {};
for _, k in pairs(keys) do
if(type(k) == "number") then table.insert(numbers, k) end;
end;
local currentSmallest;
local returnValue = {};
local redundancy;
redundancy = function()
for _, k in ipairs(numbers) do
if(not currentSmallest or k < currentSmallest) then currentSmallest = k end;
end
table.insert(returnValue, t[currentSmallest]);
module.delete(numbers, currentSmallest);
currentSmallest = nil;
if(not module.isempty(numbers)) then redundancy() end;
end
redundancy();
return returnValue;
end
-- Checks if two tables are equal.
-- ¿Why use this function?
-- Well, when you do table1 == table2, you are actually checking the memory adress and not the
-- contents of the table, this function checks the SHALLOW contents of the table
module.equals = function(...)
local arguments = module:RemoveSelf(...);
local t1 = arguments[1];
assert(t1, "Error at table(equals). Expected positional argument 1 to be a table\nGot '" .. typeof(t1) .. "' instead.");
table.remove(arguments, 1);
for k,v in pairs(t1) do
for _, t in pairs(arguments) do
if(t[k] ~= v) then
return false;
end
end
end
return true;
end
-- Checks if two tables are equal.
-- ¿Why use this function?
-- Well, when you do table1 == table2, you are actually checking the memory adress and not the
-- contents of the table, this function checks the DEEP contents of the table
module.requals = 0;
module.requals = function(...)
local arguments = module:RemoveSelf(...);
local t1 = arguments[1];
assert(t1, "Error at table(equals). Expected positional argument 1 to be a table\nGot '" .. typeof(t1) .. "' instead.");
table.remove(arguments, 1);
for k,v in pairs(t1) do
for _, t in pairs(arguments) do
if(t[k] ~= v) then --Similar to what equals does
return false;
elseif(type(v) == "table" and type(t[k]) == "table") then
if(not module.requals(v, t[k])) then
return false;
end
end
end
end
return true;
end