Module:TableUtil

local lang = mw.getLanguage('en')

local p = {}

function p.generalLength(tbl) local n = 0 for _, _ in pairs(tbl) do		n = n + 1 end return n end

function p.getKeysAndSort(tbl, f)	local ret = {} for k, _ in pairs(tbl) do		ret[#ret+1] = k	end table.sort(ret, f)	return ret end

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.hash(tbl) if not tbl then return {} end local hash = {} for k, v in pairs(tbl) do		hash[v] = k	end return hash end

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

-- sorts tblToSort to be in the same order as the elements appear in lookup function p.sortByKeyOrder(tblToSort,values) local lookup = p.hash(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, ...) -- tbl1 is modified to include the elements of tbl2 appended to the end. Order is preserved. if not tbl1 then tbl1 = {} end local newTables = {...} for _, tbl2 in ipairs(newTables) do		for _, v in ipairs(tbl2) do			tbl1[#tbl1+1] = v		end end return tbl1 end

function p.merge(tbl1, ...) -- tbl1 is modified to include all the elements of tbl2. if not tbl1 then tbl1 = {} end local tables = {...} for _, tbl2 in ipairs(tables) do		for k, v in pairs(tbl2) do			tbl1[k] = v		end 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

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

function p.removeValueFromArray(tbl, val) local len = #tbl for i = len, 1, -1 do		if tbl[i] == val then table.remove(i) end end end

function p.removeDuplicates(tbl) local hash = {} local ret = {} for _, v in ipairs(tbl) do		if not hash[v] then hash[v] = true ret[#ret+1] = v		end end return ret 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)	if s < 0 then s = #tbl + 1 + s	end if e < 0 then e = #tbl + 1 + e	end 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, ...) for k, v in pairs(tbl) do		tbl[k] = f(v, ...) end return tbl end

function p.mapArrayInPlace(tbl, f, ...) for k, v in ipairs(tbl) do		tbl[k] = f(v, ...) end return tbl end

function p.mapArraySafe(tbl, f, ...) local tbl2 = {} for k, v in ipairs(tbl) do		tbl2[k] = f(v, ...) end return tbl2 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.mapDictInPlace(tbl, f, ...) for _, v in ipairs(tbl) do		tbl[v] = f(tbl[v], ...) end return tbl end

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

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

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

-- function p.concatSafe(tbl, sep, f, ...) -- 	if not sep then sep = ',' end -- 	if f then -- 		local tbl2 = p.mapArraySafe(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

function p.alphabetize(tbl) local hash = {} local nocase = {} for i, v in ipairs(tbl) do		nocase[i] = lang:caseFold(v) hash[nocase[i]] = v	end table.sort(nocase) for i, v in ipairs(nocase) do		tbl[i] = hash[v] end return tbl end

function p.guaranteeTable(c, f, ...) if not c then return nil end if type(c) == 'table' then return f and f(c, ...) or c	end return { f and f(c, ...) or c } end

function p.guaranteeIndex(tbl, i)	if type(i) == 'number' then return i end return p.keyOf(tbl, i) end

function p.concatIfTable(tbl, sep) if not tbl then return nil end return p.concat(p.guaranteeTable(tbl), sep) end

function p.nextNonFalse(tbl) for _, v in pairs(tbl) do		if v then return v end end end

function p.interlace(tbl) local ret = {} local _, keyList = next(tbl) for key, _ in pairs(keyList) do		ret[key] = {} end for k, v in pairs(tbl) do		for key, _ in pairs(keyList) do			ret[key][k] = v[key] end end return ret end

function p.initTable(tbl, key, val) if tbl[key] then return end tbl[key] = val or {} end

function p.initDict(tbl, key, val) if tbl[key] then return end tbl[key] = val or {} tbl[#tbl+1] = key end

function p.push(tbl, val) -- useful just to make code look cleaner when tbl is something like parent[row.key] tbl[#tbl+1] = val end

function p.arrayToDict(tbl, key) for i, v in ipairs(tbl) do		tbl[v[key]] = v		tbl[i] = v	end end

return p