1091 lines
23 KiB
Lua
1091 lines
23 KiB
Lua
-- table.lua
|
|
|
|
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
|