Moonshark/core/runner/table.lua

1092 lines
23 KiB
Lua

--[[
table.lua - Extended table library functions
]]--
local table_ext = {}
-- ======================================================================
-- SET OPERATIONS
-- ======================================================================
-- Remove duplicate values (like array_unique)
function table_ext.unique(t)
if type(t) ~= "table" then return {} end
local seen = {}
local result = {}
for _, v in ipairs(t) do
if not seen[v] then
seen[v] = true
table.insert(result, v)
end
end
return result
end
-- Return items in first table that are present in all other tables (like array_intersect)
function table_ext.intersect(t1, ...)
if type(t1) ~= "table" then return {} end
local args = {...}
local result = {}
-- Convert all tables to sets for O(1) lookups
local sets = {}
for i, t in ipairs(args) do
if type(t) ~= "table" then
return {}
end
sets[i] = {}
for _, v in ipairs(t) do
sets[i][v] = true
end
end
-- Check each element in t1 against all other tables
for _, v in ipairs(t1) do
local present_in_all = true
for i = 1, #args do
if not sets[i][v] then
present_in_all = false
break
end
end
if present_in_all then
table.insert(result, v)
end
end
return result
end
-- Return items in first table that are not present in other tables (like array_diff)
function table_ext.diff(t1, ...)
if type(t1) ~= "table" then return {} end
local args = {...}
local result = {}
-- Build unified set of elements from other tables
local others = {}
for _, t in ipairs(args) do
if type(t) == "table" then
for _, v in ipairs(t) do
others[v] = true
end
end
end
-- Add elements from t1 that aren't in other tables
for _, v in ipairs(t1) do
if not others[v] then
table.insert(result, v)
end
end
return result
end
-- ======================================================================
-- SEARCH AND FILTERING
-- ======================================================================
-- Check if value exists in table (like in_array)
function table_ext.contains(t, value)
if type(t) ~= "table" then return false end
for _, v in ipairs(t) do
if v == value then
return true
end
end
return false
end
-- Find key for a value (like array_search)
function table_ext.find(t, value)
if type(t) ~= "table" then return nil end
for k, v in pairs(t) do
if v == value then
return k
end
end
return nil
end
-- Filter table elements (like array_filter)
function table_ext.filter(t, func)
if type(t) ~= "table" or type(func) ~= "function" then return {} end
local result = {}
for k, v in pairs(t) do
if func(v, k) then
if type(k) == "number" and k % 1 == 0 and k > 0 then
-- For array-like tables, maintain numerical indices
table.insert(result, v)
else
-- For associative tables, preserve the key
result[k] = v
end
end
end
return result
end
-- ======================================================================
-- TRANSFORMATION FUNCTIONS
-- ======================================================================
-- Apply a function to all values (like array_map)
function table_ext.map(t, func)
if type(t) ~= "table" or type(func) ~= "function" then return {} end
local result = {}
for k, v in pairs(t) do
if type(k) == "number" and k % 1 == 0 and k > 0 then
-- For array-like tables, maintain numerical indices
table.insert(result, func(v, k))
else
-- For associative tables, preserve the key
result[k] = func(v, k)
end
end
return result
end
-- Reduce a table to a single value (like array_reduce)
function table_ext.reduce(t, func, initial)
if type(t) ~= "table" or type(func) ~= "function" then
return initial
end
local result = initial
for k, v in pairs(t) do
if result == nil then
result = v
else
result = func(result, v, k)
end
end
return result
end
-- ======================================================================
-- ADVANCED OPERATIONS
-- ======================================================================
-- Split table into chunks (like array_chunk)
function table_ext.chunk(t, size)
if type(t) ~= "table" or type(size) ~= "number" or size <= 0 then
return {}
end
local result = {}
local chunk = {}
local count = 0
for _, v in ipairs(t) do
count = count + 1
chunk[count] = v
if count == size then
table.insert(result, chunk)
chunk = {}
count = 0
end
end
-- Add the last chunk if it has any elements
if count > 0 then
table.insert(result, chunk)
end
return result
end
-- Extract a column from a table of tables (like array_column)
function table_ext.column(t, column_key, index_key)
if type(t) ~= "table" or column_key == nil then return {} end
local result = {}
for _, row in ipairs(t) do
if type(row) == "table" and row[column_key] ~= nil then
if index_key ~= nil and row[index_key] ~= nil then
result[row[index_key]] = row[column_key]
else
table.insert(result, row[column_key])
end
end
end
return result
end
-- Merge tables (like array_merge, but preserves keys)
function table_ext.merge(...)
local result = {}
for _, t in ipairs({...}) do
if type(t) == "table" then
for k, v in pairs(t) do
if type(k) == "number" and k % 1 == 0 and k > 0 then
-- For array-like tables, append values
table.insert(result, v)
else
-- For associative tables, overwrite with latest value
result[k] = v
end
end
end
end
return result
end
-- ======================================================================
-- KEY MANIPULATION
-- ======================================================================
-- Exchange keys with values (like array_flip)
function table_ext.flip(t)
if type(t) ~= "table" then return {} end
local result = {}
for k, v in pairs(t) do
if type(v) == "string" or type(v) == "number" then
result[v] = k
end
end
return result
end
-- Get all keys from a table (like array_keys)
function table_ext.keys(t)
if type(t) ~= "table" then return {} end
local result = {}
for k, _ in pairs(t) do
table.insert(result, k)
end
return result
end
-- Get all values from a table (like array_values)
function table_ext.values(t)
if type(t) ~= "table" then return {} end
local result = {}
for _, v in pairs(t) do
table.insert(result, v)
end
return result
end
-- ======================================================================
-- STATISTICAL FUNCTIONS
-- ======================================================================
-- Sum all values (like array_sum)
function table_ext.sum(t)
if type(t) ~= "table" then return 0 end
local sum = 0
for _, v in pairs(t) do
if type(v) == "number" then
sum = sum + v
end
end
return sum
end
-- Multiply all values (like array_product)
function table_ext.product(t)
if type(t) ~= "table" then return 0 end
local product = 1
local has_number = false
for _, v in pairs(t) do
if type(v) == "number" then
product = product * v
has_number = true
end
end
return has_number and product or 0
end
-- Count value occurrences (like array_count_values)
function table_ext.count_values(t)
if type(t) ~= "table" then return {} end
local result = {}
for _, v in pairs(t) do
if type(v) == "string" or type(v) == "number" then
result[v] = (result[v] or 0) + 1
end
end
return result
end
-- ======================================================================
-- CREATION HELPERS
-- ======================================================================
-- Create a table with a range of values (like range)
function table_ext.range(start, stop, step)
if type(start) ~= "number" then return {} end
step = step or 1
local result = {}
if not stop then
stop = start
start = 1
end
if (step > 0 and start > stop) or (step < 0 and start < stop) then
return {}
end
local i = start
while (step > 0 and i <= stop) or (step < 0 and i >= stop) do
table.insert(result, i)
i = i + step
end
return result
end
-- Fill a table with a value (like array_fill)
function table_ext.fill(start_index, count, value)
if type(start_index) ~= "number" or type(count) ~= "number" or count < 0 then
return {}
end
local result = {}
for i = 0, count - 1 do
result[start_index + i] = value
end
return result
end
-- ======================================================================
-- ADDITIONAL USEFUL FUNCTIONS
-- ======================================================================
-- Reverse a table (array part only)
function table_ext.reverse(t)
if type(t) ~= "table" then return {} end
local result = {}
local count = #t
for i = count, 1, -1 do
table.insert(result, t[i])
end
return result
end
-- Get the max value in a table
function table_ext.max(t)
if type(t) ~= "table" or #t == 0 then return nil end
local max = t[1]
for i = 2, #t do
if t[i] > max then
max = t[i]
end
end
return max
end
-- Get the min value in a table
function table_ext.min(t)
if type(t) ~= "table" or #t == 0 then return nil end
local min = t[1]
for i = 2, #t do
if t[i] < min then
min = t[i]
end
end
return min
end
-- Check if all elements satisfy a condition
function table_ext.all(t, func)
if type(t) ~= "table" or type(func) ~= "function" then return false end
for k, v in pairs(t) do
if not func(v, k) then
return false
end
end
return true
end
-- Check if any element satisfies a condition
function table_ext.any(t, func)
if type(t) ~= "table" or type(func) ~= "function" then return false end
for k, v in pairs(t) do
if func(v, k) then
return true
end
end
return false
end
-- ======================================================================
-- TABLE UTILITIES
-- ======================================================================
-- Check if table is empty
function table_ext.is_empty(t)
if type(t) ~= "table" then return true end
return next(t) == nil
end
-- Get table length (works for both array and hash parts)
function table_ext.size(t)
if type(t) ~= "table" then return 0 end
local count = 0
for _ in pairs(t) do
count = count + 1
end
return count
end
-- Get a slice of a table
function table_ext.slice(t, start, stop)
if type(t) ~= "table" then return {} end
local len = #t
start = start or 1
stop = stop or len
-- Convert negative indices
if start < 0 then start = len + start + 1 end
if stop < 0 then stop = len + stop + 1 end
-- Ensure bounds
start = math.max(1, math.min(start, len + 1))
stop = math.max(0, math.min(stop, len))
local result = {}
for i = start, stop do
table.insert(result, t[i])
end
return result
end
-- ======================================================================
-- SORTING FUNCTIONS
-- ======================================================================
-- Sort array values (like sort)
function table_ext.sort(t)
if type(t) ~= "table" then return t end
table.sort(t)
return t
end
-- Sort array values in reverse order (like rsort)
function table_ext.rsort(t)
if type(t) ~= "table" then return t end
table.sort(t, function(a, b) return a > b end)
return t
end
-- Sort and maintain index association (like asort)
function table_ext.asort(t)
if type(t) ~= "table" then return t end
local keys, result = {}, {}
for k in pairs(t) do
table.insert(keys, k)
end
table.sort(keys, function(a, b)
return t[a] < t[b]
end)
for _, k in ipairs(keys) do
result[k] = t[k]
end
return result
end
-- Sort in reverse order and maintain index association (like arsort)
function table_ext.arsort(t)
if type(t) ~= "table" then return t end
local keys, result = {}, {}
for k in pairs(t) do
table.insert(keys, k)
end
table.sort(keys, function(a, b)
return t[a] > t[b]
end)
for _, k in ipairs(keys) do
result[k] = t[k]
end
return result
end
-- Sort by keys (like ksort)
function table_ext.ksort(t)
if type(t) ~= "table" then return t end
local keys, result = {}, {}
for k in pairs(t) do
table.insert(keys, k)
end
table.sort(keys)
for _, k in ipairs(keys) do
result[k] = t[k]
end
return result
end
-- Sort by keys in reverse order (like krsort)
function table_ext.krsort(t)
if type(t) ~= "table" then return t end
local keys, result = {}, {}
for k in pairs(t) do
table.insert(keys, k)
end
table.sort(keys, function(a, b) return a > b end)
for _, k in ipairs(keys) do
result[k] = t[k]
end
return result
end
-- Sort using custom comparison function (like usort)
function table_ext.usort(t, compare_func)
if type(t) ~= "table" or type(compare_func) ~= "function" then return t end
table.sort(t, compare_func)
return t
end
-- Sort maintaining keys using custom comparison function (like uasort)
function table_ext.uasort(t, compare_func)
if type(t) ~= "table" or type(compare_func) ~= "function" then return t end
local keys, result = {}, {}
for k in pairs(t) do
table.insert(keys, k)
end
table.sort(keys, function(a, b)
return compare_func(t[a], t[b])
end)
for _, k in ipairs(keys) do
result[k] = t[k]
end
return result
end
-- Sort by keys using custom comparison function (like uksort)
function table_ext.uksort(t, compare_func)
if type(t) ~= "table" or type(compare_func) ~= "function" then return t end
local keys, result = {}, {}
for k in pairs(t) do
table.insert(keys, k)
end
table.sort(keys, compare_func)
for _, k in ipairs(keys) do
result[k] = t[k]
end
return result
end
-- Natural order sort (like natsort)
function table_ext.natsort(t)
if type(t) ~= "table" then return t end
local function natural_compare(a, b)
local function get_chunks(s)
if type(s) ~= "string" then s = tostring(s) end
local chunks = {}
for num, alpha in s:gmatch("(%d+)([^%d]*)") do
table.insert(chunks, {n=true, val=tonumber(num)})
if alpha ~= "" then
table.insert(chunks, {n=false, val=alpha})
end
end
return chunks
end
local a_chunks = get_chunks(a)
local b_chunks = get_chunks(b)
for i = 1, math.min(#a_chunks, #b_chunks) do
if a_chunks[i].n ~= b_chunks[i].n then
return a_chunks[i].n -- numbers come before strings
elseif a_chunks[i].val ~= b_chunks[i].val then
if a_chunks[i].n then
return a_chunks[i].val < b_chunks[i].val
else
return a_chunks[i].val < b_chunks[i].val
end
end
end
return #a_chunks < #b_chunks
end
table.sort(t, natural_compare)
return t
end
-- Natural case-insensitive sort (like natcasesort)
function table_ext.natcasesort(t)
if type(t) ~= "table" then return t end
local function case_insensitive_natural_compare(a, b)
if type(a) == "string" and type(b) == "string" then
return table_ext.natural_compare(a:lower(), b:lower())
else
return table_ext.natural_compare(a, b)
end
end
return table_ext.usort(t, case_insensitive_natural_compare)
end
-- ======================================================================
-- ARRAY MODIFICATION FUNCTIONS
-- ======================================================================
-- Push one or more elements onto the end (like array_push)
function table_ext.push(t, ...)
if type(t) ~= "table" then return 0 end
local count = 0
for _, v in ipairs({...}) do
table.insert(t, v)
count = count + 1
end
return count
end
-- Pop the element off the end (like array_pop)
function table_ext.pop(t)
if type(t) ~= "table" or #t == 0 then return nil end
local value = t[#t]
t[#t] = nil
return value
end
-- Shift an element off the beginning (like array_shift)
function table_ext.shift(t)
if type(t) ~= "table" or #t == 0 then return nil end
local value = t[1]
table.remove(t, 1)
return value
end
-- Prepend elements to the beginning (like array_unshift)
function table_ext.unshift(t, ...)
if type(t) ~= "table" then return 0 end
local args = {...}
for i = #args, 1, -1 do
table.insert(t, 1, args[i])
end
return #t
end
-- Pad array to specified length (like array_pad)
function table_ext.pad(t, size, value)
if type(t) ~= "table" then return {} end
local result = table_ext.deep_copy(t)
local current_size = #result
if size == current_size then return result end
if size > current_size then
-- Pad to the right
for i = current_size + 1, size do
result[i] = value
end
else
-- Pad to the left (negative size)
local abs_size = math.abs(size)
if abs_size < current_size then
local temp = {}
for i = 1, abs_size do
if i <= abs_size - current_size then
temp[i] = value
else
temp[i] = result[i - (abs_size - current_size)]
end
end
result = temp
else
-- Fill completely with padding value
result = {}
for i = 1, abs_size do
result[i] = value
end
end
end
return result
end
-- Remove a portion and replace it (like array_splice)
function table_ext.splice(t, offset, length, ...)
if type(t) ~= "table" then return {} end
local result = table_ext.deep_copy(t)
local size = #result
-- Handle negative offset
if offset < 0 then
offset = size + offset
end
-- Ensure offset is valid
offset = math.max(1, math.min(offset, size + 1))
-- Handle negative or nil length
if length == nil then
length = size - offset + 1
elseif length < 0 then
length = math.max(0, size - offset + length + 1)
end
-- Extract removed portion
local removed = {}
for i = offset, offset + length - 1 do
if i <= size then
table.insert(removed, result[i])
end
end
-- Remove portion from original
for i = 1, length do
table.remove(result, offset)
end
-- Insert replacement values
local replacements = {...}
for i = #replacements, 1, -1 do
table.insert(result, offset, replacements[i])
end
return removed, result
end
-- Randomize array order (like shuffle)
function table_ext.shuffle(t)
if type(t) ~= "table" then return t end
local result = table_ext.deep_copy(t)
local size = #result
for i = size, 2, -1 do
local j = math.random(i)
result[i], result[j] = result[j], result[i]
end
return result
end
-- Pick random keys from array (like array_rand)
function table_ext.rand(t, num_keys)
if type(t) ~= "table" then return nil end
local size = #t
if size == 0 then return nil end
num_keys = num_keys or 1
num_keys = math.min(num_keys, size)
if num_keys <= 0 then return nil end
if num_keys == 1 then
return math.random(size)
else
local keys = {}
local result = {}
-- Create a list of all possible keys
for i = 1, size do
keys[i] = i
end
-- Select random keys
for i = 1, num_keys do
local j = math.random(#keys)
table.insert(result, keys[j])
table.remove(keys, j)
end
table.sort(result)
return result
end
end
-- ======================================================================
-- ARRAY INSPECTION FUNCTIONS
-- ======================================================================
-- Check if key exists (like array_key_exists)
function table_ext.key_exists(key, t)
if type(t) ~= "table" then return false end
return t[key] ~= nil
end
-- Get the first key (like array_key_first)
function table_ext.key_first(t)
if type(t) ~= "table" then return nil end
-- For array-like tables
if #t > 0 then return 1 end
-- For associative tables
local first_key = nil
for k in pairs(t) do
first_key = k
break
end
return first_key
end
-- Get the last key (like array_key_last)
function table_ext.key_last(t)
if type(t) ~= "table" then return nil end
-- For array-like tables
if #t > 0 then return #t end
-- For associative tables (no guaranteed order, return any key)
local last_key = nil
for k in pairs(t) do
last_key = k
end
return last_key
end
-- Check if table is a list (like array_is_list)
function table_ext.is_list(t)
if type(t) ~= "table" then return false end
local count = 0
for k in pairs(t) do
count = count + 1
if type(k) ~= "number" or k <= 0 or math.floor(k) ~= k or k > count then
return false
end
end
return true
end
-- ======================================================================
-- OTHER IMPORTANT FUNCTIONS
-- ======================================================================
-- Create array with keys from one array, values from another (like array_combine)
function table_ext.combine(keys, values)
if type(keys) ~= "table" or type(values) ~= "table" then return {} end
local result = {}
local key_count = #keys
local value_count = #values
for i = 1, math.min(key_count, value_count) do
result[keys[i]] = values[i]
end
return result
end
-- Replace elements from one array into another (like array_replace)
function table_ext.replace(t, ...)
if type(t) ~= "table" then return {} end
local result = table_ext.deep_copy(t)
for _, replacement in ipairs({...}) do
if type(replacement) == "table" then
for k, v in pairs(replacement) do
result[k] = v
end
end
end
return result
end
-- Replace elements recursively (like array_replace_recursive)
function table_ext.replace_recursive(t, ...)
if type(t) ~= "table" then return {} end
local result = table_ext.deep_copy(t)
for _, replacement in ipairs({...}) do
if type(replacement) == "table" then
for k, v in pairs(replacement) do
if type(v) == "table" and type(result[k]) == "table" then
result[k] = table_ext.replace_recursive(result[k], v)
else
result[k] = v
end
end
end
end
return result
end
-- Apply function to each element (like array_walk)
function table_ext.walk(t, callback, user_data)
if type(t) ~= "table" or type(callback) ~= "function" then return t end
for k, v in pairs(t) do
t[k] = callback(v, k, user_data)
end
return t
end
-- Apply function recursively (like array_walk_recursive)
function table_ext.walk_recursive(t, callback, user_data)
if type(t) ~= "table" or type(callback) ~= "function" then return t end
for k, v in pairs(t) do
if type(v) == "table" then
table_ext.walk_recursive(v, callback, user_data)
else
t[k] = callback(v, k, user_data)
end
end
return t
end
-- Sort multiple arrays (simplified array_multisort)
function table_ext.multisort(...)
local args = {...}
if #args == 0 then return end
-- First argument is the main table
local main = args[1]
if type(main) ~= "table" then return end
-- Create a table of indices
local indices = {}
for i = 1, #main do
indices[i] = i
end
-- Sort the indices based on the arrays
table.sort(indices, function(a, b)
for i = 1, #args do
local arr = args[i]
if type(arr) == "table" then
if arr[a] ~= arr[b] then
return arr[a] < arr[b]
end
end
end
return a < b
end)
-- Reorder all arrays based on sorted indices
for i = 1, #args do
local arr = args[i]
if type(arr) == "table" then
local temp = table_ext.deep_copy(arr)
for j = 1, #indices do
arr[j] = temp[indices[j]]
end
end
end
end
-- Efficient deep copy function
function table_ext.deep_copy(obj)
if type(obj) ~= 'table' then return obj end
local res = {}
for k, v in pairs(obj) do res[k] = table_ext.deep_copy(v) end
return res
end
-- ======================================================================
-- INSTALL EXTENSIONS INTO TABLE LIBRARY
-- ======================================================================
for name, func in pairs(table) do
table_ext[name] = func
end
return table_ext