-- -- table.lua -- Additions to Lua's built-in table functions. -- Copyright (c) 2002-2013 Jason Perkins and the Premake project -- -- -- Make a copy of the indexed elements of the table. -- function table.arraycopy(object) local result = {} for i, value in ipairs(object) do result[i] = value end return result end -- -- Returns true if the table contains the specified value. -- function table.contains(t, value) for _,v in pairs(t) do if (v == value) then return true end end return false end -- -- Make a shallow copy of a table -- function table.shallowcopy(object) local copy = {} for k, v in pairs(object) do copy[k] = v end return copy end -- -- Make a complete copy of a table, including any child tables it contains. -- function table.deepcopy(object) -- keep track of already seen objects to avoid loops local seen = {} local function copy(object) if type(object) ~= "table" then return object elseif seen[object] then return seen[object] end local clone = {} seen[object] = clone for key, value in pairs(object) do clone[key] = copy(value) end setmetatable(clone, getmetatable(object)) return clone end return copy(object) end -- -- Enumerates an array of objects and returns a new table containing -- only the value of one particular field. -- function table.extract(arr, fname) local result = { } for _,v in ipairs(arr) do table.insert(result, v[fname]) end return result end -- -- Enumerates an array of objects and returns a new table containing -- only the values satisfying the given predicate. -- function table.filter(arr, fn) local result = { } table.foreachi(arr, function(val) if fn(val) then table.insert(result, val) end end) return result end -- -- Flattens a hierarchy of tables into a single array containing all -- of the values. -- function table.flatten(arr) local result = {} local function flatten(arr) local n = #arr for i = 1, n do local v = arr[i] if type(v) == "table" then flatten(v) elseif v then table.insert(result, v) end end end flatten(arr) return result end -- -- Walk the elements of an array and call the specified function -- for each non-nil element. This works around a "feature" of the -- ipairs() function that stops iteration at the first nil. -- -- @param arr -- The array to iterate. -- @param func -- The function to call. The value (not the index) will be passed -- as the only argument. -- function table.foreachi(arr, func) if arr then local n = #arr for i = 1, n do local v = arr[i] if v then func(v, i) end end end end -- -- Merge two lists into an array of objects, containing pairs -- of values, one from each list. -- function table.fold(list1, list2) local result = {} for _, item1 in ipairs(list1 or {}) do if list2 and #list2 > 0 then for _, item2 in ipairs(list2) do table.insert(result, { item1, item2 }) end else table.insert(result, { item1 }) end end return result end -- -- Merges an array of items into a string. -- function table.implode(arr, before, after, between) local result = "" for _,v in ipairs(arr) do if (result ~= "" and between) then result = result .. between end result = result .. before .. v .. after end return result end -- -- Looks for an object within an array. Returns its index if found, -- or nil if the object could not be found. -- function table.indexof(tbl, obj) for k, v in ipairs(tbl) do if v == obj then return k end end end -- -- Looks for an object within a table. Returns the key if found, -- or nil if the object could not be found. -- function table.findKeyByValue(tbl, obj) for k, v in pairs(tbl) do if v == obj then return k end end end --- -- Insert a new value into a table in the position after the specified -- existing value. If the specified value does not exist in the table, -- the new value is appended to the end of the table. -- -- @param tbl -- The table in which to insert. -- @param after -- The existing value to insert after. -- @param value -- The new value to insert. -- function table.insertafter(tbl, after, value) local i = table.indexof(tbl, after) if i then table.insert(tbl, i + 1, value) else table.insert(tbl, value) end end -- -- Inserts a value or array of values into a table. If the value is -- itself a table, its contents are enumerated and added instead. So -- these inputs give these outputs: -- -- "x" -> { "x" } -- { "x", "y" } -> { "x", "y" } -- { "x", { "y" }} -> { "x", "y" } -- function table.insertflat(tbl, values) if values == nil then return elseif type(values) == "table" then for _, value in ipairs(values) do table.insertflat(tbl, value) end else table.insert(tbl, values) end return tbl end -- -- Inserts a value into a table as both a list item and a key-value pair. -- Useful for set operations. Returns false if the value already exists, true otherwise. -- function table.insertkeyed(tbl, pos, value) if value == nil then value = pos pos = #tbl + 1 end if tbl[value] ~= nil then return false end table.insert(tbl, pos, value) tbl[value] = value return true end -- -- Inserts a value into a table in sorted order. Assumes that the -- table is already sorted according to the sort function. If fn is -- nil, the table is sorted according to the < operator. -- function table.insertsorted(tbl, value, fn) if value == nil then return else fn = fn or function(a, b) return a < b end local minindex = 1 local maxindex = #tbl + 1 while minindex < maxindex do local index = minindex + ((maxindex - minindex) >> 1) local test = tbl[index] if fn(value, test) then maxindex = index else minindex = index + 1 if not fn(test, value) then break end end end table.insert(tbl, minindex, value) end return tbl end -- -- Returns true if the table is empty, and contains no indexed or keyed values. -- function table.isempty(t) return next(t) == nil end -- -- Adds the values from one array to the end of another and -- returns the result. -- function table.join(...) local result = { } local arg = {...} for _,t in ipairs(arg) do if type(t) == "table" then for _,v in ipairs(t) do table.insert(result, v) end else table.insert(result, t) end end return result end -- -- Return a list of all keys used in a table. -- function table.keys(tbl) local keys = {} for k, _ in pairs(tbl) do table.insert(keys, k) end return keys end -- -- Adds the key-value associations from one table into another -- and returns the resulting merged table. -- function table.merge(...) local result = {} local arg = {...} for _,t in ipairs(arg) do if type(t) == "table" then for k,v in pairs(t) do if type(result[k]) == "table" and type(v) == "table" then result[k] = table.merge(result[k], v) else result[k] = v end end else error("invalid value") end end return result end --- -- Replace all instances of `value` with `replacement` in an array. Array -- elements are modified in place. -- -- @param value -- The value to be replaced. -- @param replacement -- The new value. --- function table.replace(self, value, replacement) for i = 1, #self do if self[i] == value then self[i] = replacement end end end -- -- Translates the values contained in array, using the specified -- translation table, and returns the results in a new array. -- function table.translate(arr, translation) if not translation then return {} end local result = {} for i = 1, #arr do local tvalue if type(translation) == "function" then tvalue = translation(arr[i]) else tvalue = translation[arr[i]] end if (tvalue) then table.insert(result, tvalue) end end return result end -- -- Dumps a table to a string -- function table.tostring(tab, recurse, indent) local res = '' if not indent then indent = 0 end local format_value = function(k, v, i) formatting = string.rep("\t", i) if k then if type(k) == "table" then k = '[table]' end formatting = formatting .. k .. ": " end if not v then return formatting .. '(nil)' elseif type(v) == "table" then if recurse and recurse > 0 then return formatting .. '\n' .. table.tostring(v, recurse-1, i+1) else return formatting .. "" end elseif type(v) == "function" then return formatting .. tostring(v) elseif type(v) == "userdata" then return formatting .. "" elseif type(v) == "boolean" then if v then return formatting .. 'true' else return formatting .. 'false' end else return formatting .. v end end if type(tab) == "table" then local first = true -- add the meta table. local mt = getmetatable(tab) if mt then res = res .. format_value('__mt', mt, indent) first = false end -- add all values. for k, v in pairs(tab) do if not first then res = res .. '\n' end res = res .. format_value(k, v, indent) first = false end else res = res .. format_value(nil, tab, indent) end return res end -- -- Returns a copy of a list with all duplicate elements removed. -- function table.unique(tab) local elems = { } local result = { } table.foreachi(tab, function(elem) if not elems[elem] then table.insert(result, elem) elems[elem] = true end end) return result end -- -- Filters a table for empty entries. primarly useful for lists of string. -- function table.filterempty(dirs) return table.translate(dirs, function(val) if val and #val > 0 then return val else return nil end end) end -- -- Compares two tables. -- function table.equals(a, b) for k, v in pairs(a) do if b[k] ~= v then return false end end for k, v in pairs(b) do if a[k] ~= v then return false end end return true end -- -- Enumerate a table sorted by its keys. -- function spairs(t) -- collect the keys local keys = {} for k in pairs(t) do table.insert(keys, k) end table.sort(keys) -- return the iterator function local i = 0 return function() i = i + 1 if keys[i] then return keys[i], t[keys[i]] end end end -- -- Intersect two arrays and return a new array -- function table.intersect(a, b) local result = {} for _, v in ipairs(b) do if table.indexof(a, v) then table.insert(result, v) end end return result end -- -- The difference of A and B is the set containing those elements that are in A but not in B -- function table.difference(a, b) local result = {} for _, v in ipairs(a) do if not table.indexof(b, v) then table.insert(result, v) end end return result end