Module:TableUtil

local checkType = require("libraryUtil").checkType; local util_text = require('Module:TextUtil')

local floor = math.floor local infinity = math.huge

local p = {}

function p.keyOf(tbl, val) for k, v in pairs(tbl) do		if v == val then return k		end end return nil end

function p.lookup(tbl) local lookup = {} for k, v in pairs(tbl) do		lookup[v] = k	end return lookup end

function p.appendLookup(tbl, parent) for k, v in pairs(tbl) do		parent[v] = k	end return end

-- sorts tblToSort to be in the same order as the elements appear in lookup function p.sortByKeyOrder(tblToSort,values) local lookup = p.lookup(values) table.sort(tblToSort, function (a,b)			return (lookup[a] or 0) < (lookup[b] or 0)		end	) return end

function p.sortUnique(tbl) table.sort(tbl) local tbl2 = {} local i = 0 for k, v in ipairs(tbl) do		if v ~= tbl2[i] then i = i + 1 tbl2[i] = v		end end return tbl2 end

function p.mergeArrays(tbl1,tbl2) -- tbl1 is modified to include the elements of tbl2 appended to the end. Order is preserved. if not tbl1 then tbl1 = {} end if not tbl2 then tbl2 = {} end for _, v in ipairs(tbl2) do		tbl1[#tbl1+1] = v	end return tbl1 end

function p.merge(tbl1, tbl2) -- tbl1 is modified to include all the elements of tbl2. if not tbl1 then tbl1 = {} end if not tbl2 then tbl2 = {} end for k, v in pairs(tbl2) do		tbl1[k] = v	end return tbl1 end

-- table.remove for non-integer key function p.remove(tbl, key) local output = tbl[key] tbl[key] = nil return output end

-- returns a copy of tbl with the elements in opposite order (not a deep copy) function p.reverse(tbl) local tbl2 = {} local len = #tbl for i = len, 1, -1 do		tbl2[len - i + 1] = tbl[i] end return tbl2 end

function p.reverseInPlace(tbl) local len = #tbl local stop_at = len / 2 for i = 1, stop_at do		local temp = tbl[i] tbl[i] = tbl[len - i + 1] tbl[len - i + 1] = temp end end

function p.shallowClone(tbl) -- mostly to be able to use # operator on something from mw.loadData local tbl2 = {} for k, v in pairs(tbl) do		tbl2[k] = v	end return tbl2 end

function p.slice(tbl, s, e)	local tbl2 = {} for k = s, e do		tbl2[#tbl2+1] = tbl[k] end return tbl2 end

-- prints the table as a comma-separated list with and function p.printList(tbl) if #tbl == 1 then return tbl[1] elseif #tbl == 2 then return table.concat(tbl, ' and ') else last = table.remove(tbl, #tbl) list = table.concat(tbl, ', ') return list .. ', and ' .. (last or '') end end

function p.removeFalseEntries(tbl, max) if not max then max = #tbl end local j = 0 for i = 1, max do		if tbl[i] then j = j + 1 tbl[j] = tbl[i] end end for i = j+1, max do		tbl[i] = nil end return tbl end

function p.padFalseEntries(tbl, max, default) default = default or '' for i = 1, max do		if not tbl[i] then tbl[i] = default end end return tbl end

function p.mapInPlace(tbl, f, max) for k, v in pairs(tbl) do		tbl[k] = f(v) end return tbl end

function p.mapSafe(tbl, f)	local tbl2 = mw.clone(tbl) p.removeFalseEntries(tbl2) for k, v in ipairs(tbl2) do		tbl2[k] = f(v) end return tbl2 end

function p.mapRowsInPlace(tbl, f)	for k, row in ipairs(tbl) do		f(row) end return tbl end

function p.mapDictRowsInPlace(tbl, f)	for _, v in ipairs(tbl) do		f(tbl[v]) end return tbl end

function p.splitAndMap(str, f, sep) sep = '%s*' .. (sep or ',') .. '%s*' return p.mapInPlace(util_text.split(str, sep), f) end

function p.concat(tbl, sep, f, max) -- f is a function that doesn't take any additional args if f then p.mapInPlace(tbl, f)	else p.removeFalseEntries(tbl, max) end return table.concat(tbl, sep) end

function p.concatSafe(tbl, sep, f) -- f is a function that doesn't take any additional args local tbl2 = mw.clone(tbl) if f then p.mapInPlace(tbl2, f)	else p.removeFalseEntries(tbl2) end return table.concat(tbl2, sep) end

function p.concatFromArgs(args, argname, sep, f)	-- if fields are saved in args as field1, field2, field3, etc local i = 1 local tbl = {} if args[argname] then tbl[1] = args[argname] i = 2 end while args[argname .. i] do tbl[i] = args[argname .. i]		i = i + 1 end return next(tbl) and p.concat(tbl, sep, f) end

function p.crop(tbl, max) for k, _ in ipairs(tbl) do		if k > max then tbl[k] = nil end end end

--[[

-- isPositiveInteger -- -- This function returns true if the given value is a positive integer, and false -- if not. Although it doesn't operate on tables, it is included here as it is -- useful for determining whether a given table key is in the array part or the -- hash part of a table.

--]] function p.isPositiveInteger(v) if type(v) == 'number' and v >= 1 and floor(v) == v and v < infinity then return true else return false end end

--[[

-- numKeys -- -- This takes a table and returns an array containing the numbers of any -- numerical keys that have non-nil values, sorted in numerical order.

--]] function p.numKeys(t) checkType('numKeys', 1, t, 'table') local isPositiveInteger = p.isPositiveInteger local nums = {} for k, v in pairs(t) do		if isPositiveInteger(k) then nums[#nums + 1] = k		end end table.sort(nums) return nums end

--[[

-- compressSparseArray -- -- This takes an array with one or more nil values, and removes the nil values -- while preserving the order, so that the array can be safely traversed with -- ipairs.

--]] function p.compressSparseArray(t) checkType('compressSparseArray', 1, t, 'table') local ret = {} local nums = p.numKeys(t) for _, num in ipairs(nums) do		ret[#ret + 1] = t[num] end return ret end

--[[

-- sparseIpairs -- -- This is an iterator for sparse arrays. It can be used like ipairs, but can -- handle nil values.

--]] function p.sparseIpairs(t) checkType('sparseIpairs', 1, t, 'table') local nums = p.numKeys(t) local i = 0 local lim = #nums return function i = i + 1 if i <= lim then local key = nums[i] return key, t[key] else return nil, nil end end end

return p