Documentation for this module may be created at Module:UtilsTable/doc
local p = {} local h = {} -- HELPERS function h.append(tbl, val) tbl[table.maxn(tbl)+1] = val end function h.mapper(iteratee) if type(iteratee) == "function" then return iteratee end if type(iteratee) == "string" or type(iteratee) == "number" then return p._property(iteratee) end end function h.predicate(iteratee) if type(iteratee) == "function" then return iteratee end if type(iteratee) == "string" or type(iteratee) == "number" then return p._property(iteratee) end if type(iteratee) == "table" then return p._isMatch(iteratee) end return iteratee end -- GENERAL function p.clone(val) if type(val) ~= "table" then return val end local result = {} for k, v in pairs(val) do result[k] = v end return result end function p.cloneDeep(val) if type(val) ~= "table" then return val end return p.merge({}, val) end function p.hasArray(tbl) if type(tbl) ~= "table" then return false end return p.size(tbl) == 0 or p.len(tbl) > 0 end function p.hasKey(tbl, key) return p._hasKey(key)(tbl) end function p._hasKey(key) return function(tbl) return not not tbl[key] end end function p.invert(tbl) local inverted = {} for k, v in pairs(tbl) do inverted[v] = k end return inverted end function p.isArray(tbl) if type(tbl) ~= "table" then return false end for k, v in pairs(tbl) do if type(k) ~= "number" then return false end end return true end function p.isEmpty(tbl) return p.size(tbl) == 0 end function p.isEqual(tbl, other) return p._isEqual(tbl)(other) end function p._isEqual(tbl) return function(other) if type(tbl) ~= "table" or type(other) ~= "table" then return tbl == other end return p.isMatch(other, tbl) and p.isMatch(tbl, other) end end function p.isMatch(tbl, source) return p._isMatch(source)(tbl) end function p._isMatch(sourceTbl) return function(tbl) if sourceTbl == tbl then return true end if type(sourceTbl) ~= type(tbl) then return false end if p.size(sourceTbl) == 0 and p.size(tbl) > 0 then return false end for k, v in pairs(sourceTbl) do if type(v) ~= "table" and v ~= tbl[k] then return false end if type(v) == "table" and not p.isMatch(tbl[k], v) then return false end end return true end end function p.ivalues(tbl) local result = {} for _, v in ipairs(tbl) do table.insert(result, v) end return result 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.keys(tbl) local result = {} for k in pairs(tbl) do h.append(result, k) end return result end function p.mapValues(tbl, iteratee) return p._mapValues(iteratee)(tbl) end function p._mapValues(iteratee) local mappingFunction = h.mapper(iteratee) return function(tbl) local result = {} for k, v in pairs(tbl) do result[k] = mappingFunction(v) end return result end end function p.merge(tbl, ...) if tbl == nil then return nil end for _, source in ipairs({...}) do for k, v in pairs(source) do if type(v) == "table" and type(tbl[k]) == "table" then tbl[k] = p.merge({}, tbl[k], v) else tbl[k] = p.clone(v) end end end return tbl end local inspect local MAX_SINGLELINE = 50 -- If changing this, also change the variable of the same name at [[Module:UtilsTable/Documentation/Data]] function p.print(tbl, singleLine) inspect = inspect or require("Module:UtilsTable/Inspect") -- lazy-loaded for performance optimization return inspect(tbl, { multiline = function(t) if singleLine then return false end local size = 0 for key, val in pairs(t) do if type(val) == "table" then return true end size = size + 1 end local singleLineLength = #inspect(t, { multiline = false }) return singleLineLength > MAX_SINGLELINE or size > #t and size - #t > 1 end }) end function p.printPath(pathComponents) local path = "" for _, pathComponent in pairs(pathComponents or {}) do if string.match(pathComponent, "^[A-Za-z_][A-Za-z_0-9]*$") then path = path .. "." .. pathComponent elseif string.match(pathComponent, "^%[.*%]$") then path = path .. pathComponent else path = path .. "[" .. p.print(pathComponent) .. "]" end end return path end function p.property(tbl, path) return p._property(path)(tbl) end function p._property(path) return function(tbl) if type(path) ~= "table" then path = { path } end local result = tbl for i, key in ipairs(path) do result = result[key] or result[mw.text.trim(key, '%[%]"')] if result == nil then return result end end return result end end function p.size(tbl) return #p.keys(tbl) end function p.stringKeys(tbl) local result = {} for k in pairs(tbl) do if type(k) == "string" then table.insert(result, k) end end return result end function p.toArray(tbl, keys, padValue) return p._toArray(keys, padValue)(tbl) end function p._toArray(keys, padValue) return function(tbl) local array = {} if keys then for i, key in ipairs(keys) do array[i] = tbl[key] or padValue end else for k, v in pairs(tbl) do table.insert(array, v) end end return array end end --[[ "Array" functions (using the `ipairs` iterator, mostly) --]] function p.compact(array) local result = {} local j = 1 for i = 1, table.maxn(array) do if array[i] then result[j] = array[i] j = j + 1 end end return result end function p.concat(array, ...) if type(array) ~= "table" then array = {array} end local result = p.clone(array) for i, arrayOrValue in ipairs({...}) do if not p.hasArray(arrayOrValue) then h.append(result, arrayOrValue) else for _, value in ipairs(arrayOrValue) do h.append(result, value) end end end return result end function p.difference(array, other) local result = {} local lookup = p.invert(other) for i, v in ipairs(array) do if not lookup[v] then table.insert(result, v) end end return result end function p.dropWhile(array, predicate) return p._dropWhile(predicate)(array) end function p._dropWhile(predicate) local predicate = h.predicate(predicate) return function(array) local i = 1 while i <= #array and predicate(array[i], i) do i = i + 1 end return p.slice(array, i) end end function p.dropRightWhile(array, predicate) return p._dropRightWhile(predicate)(array) end function p._dropRightWhile(predicate) local predicate = h.predicate(predicate) return function(array) local result = {} local i = p.len(array) while i > 0 and predicate(array[i], i) do i = i - 1 end while i > 0 do table.insert(result, 1, array[i]) i = i - 1 end return result end end function p.filter(array, iteratee) return p._filter(iteratee)(array) end function p._filter(iteratee) return function(array) local predicateFn = h.predicate(iteratee) local results = {} for i, v in ipairs(array) do if predicateFn(v) then table.insert(results, v) end end return results end end function p.find(array, iteratee) return p._find(iteratee)(array) end function p._find(iteratee) local predicate = h.predicate(iteratee) return function(array) for i, v in ipairs(array) do if predicate(v) then return v, i end end return nil, nil end end function p.findIndex(array, iteratee) local index = select(2, p.find(array, iteratee)) return index end function p.findLast(array, iteratee) return p._findLast(iteratee)(array) end function p._findLast(iteratee) local predicate = h.predicate(iteratee) return function(array) local v, i = p.find(p.reverse(array), predicate) if not i then return nil, nil end return v, p.len(array) - i + 1 end end function p.flatten(array) local result = {} for _, v in ipairs(array) do result = p.concat(result, v) end return result end function p.flatMap(array, mappingFunction) return p._flatMap(mappingFunction)(array) end function p._flatMap(mappingFunction) return function(array) return p.flatten(p.map(array, mappingFunction)) end end function p.fromPairs(pairs) local result = {} for i, pair in ipairs(pairs) do result[pair[1]] = pair[2] end return result end function p.groupBy(array, iteratee) return p._groupBy(iteratee)(array) end function p._groupBy(iteratee) local mappingFn = h.mapper(iteratee) return function(array) local result = {} for _, v in ipairs(array) do local groupingKey = mappingFn(v) if groupingKey then local group = result[groupingKey] or {} result[groupingKey] = p.concat(group, v) end end return result end end function p.includes(array, value) for _, v in ipairs(array) do if v == value then return true end end return false end function p.intersection(array, other) local result = {} for i, v in ipairs(array) do if p.keyOf(other, v) then table.insert(result, v) end end return result end function p.keyBy(array, iteratee) return p._keyBy(iteratee)(array) end function p._keyBy(iteratee) local mapper = h.mapper(iteratee) return function(array) local result = {} for i, v in ipairs(array) do result[mapper(v)] = v end return result end end function p.len(array) return #p.clone(array) end function p.map(array, iteratee) return p._map(iteratee)(array) end function p._map(iteratee) return function(array) local mappingFunction = iteratee if type(iteratee) == "string" then mappingFunction = function(val) return val[iteratee] end end local tbl2 = {} for k, v in ipairs(array) do tbl2[k] = mappingFunction(v) end return tbl2 end end function p.min(array) local min = array[1] for _, v in ipairs(array) do min = math.min(min, v) end return min end function p.max(array) local max = array[1] for _, v in ipairs(array) do max = math.max(max, v) end return max end function p.padNils(array, padValue, max) return p._padNils(padValue, max)(array) end function p._padNils(padValue, max) return function(array) padValue = padValue or '' max = max or table.maxn(array) local result = p.clone(array) for i = 1, max do if result[i] == nil then result[i] = padValue end end return result end end function p.partition(array, iteratee) return p._partition(iteratee)(array) end function p._partition(iteratee) return function(array) local predicateFn = h.predicate(iteratee) local trueResults = {} local falseResults = {} for i, v in ipairs(array) do if predicateFn(v) then table.insert(trueResults, v) else table.insert(falseResults, v) end end return trueResults, falseResults end end -- returns a copy of tbl with the elements in opposite order (not a deep copy) function p.reverse(array) local tbl2 = {} local len = p.len(array) for i = len, 1, -1 do tbl2[len - i + 1] = array[i] end return tbl2 end function p.slice(array, s, e) return p._slice(s, e)(array) end function p._slice(s, e) return function(array) local tbl2 = {} e = e or p.len(array) local j = 1 for i = s, e do tbl2[j] = array[i] i = i + 1 j = j + 1 end return tbl2 end end function p.sortBy(array, iteratees) return p._sortBy(iteratees)(array) end function p._sortBy(iteratees) if type(iteratees) ~= "table" then iteratees = {iteratees} end local comparators = {} for i, iteratee in ipairs(iteratees) do local mappingFn = h.mapper(iteratee) comparators[i] = h.comparator(mappingFn) end return function(array) local result = p.clone(array) table.sort(result, function(a, b) local isEqual, isLessThan local i = 1 repeat isEqual, isLessThan = comparators[i](a, b) i = i + 1 until not isEqual or i > #iteratees return isLessThan end) return result end end function h.comparator(mappingFn) return function(a, b) a = mappingFn(a) b = mappingFn(b) return a == b, a < b end end function p.tail(array) local result = {} for i in ipairs(array) do if i > 1 then result[i - 1] = array[i] end end return result end function p.take(array, n) return p._take(n)(array) end function p._take(n) return function(array) return p.slice(array, 1, n) end end function p.takeWhile(array, predicate) return p._takeWhile(predicate)(array) end function p._takeWhile(predicate) local predicate = h.predicate(predicate) return function(array) local result = {} local i = 1 while i <= p.len(array) and predicate(array[i], i) do result[i] = array[i] i = i + 1 end return result end end function p.union(arrays) local result = {} local seen = {} for _, array in ipairs(arrays) do for _, value in ipairs(array) do if not seen[value] then table.insert(result, value) seen[value] = true end end end return result end function p.unique(array) local result = {} for _, v in ipairs(array) do if not p.find(result, p._isEqual(v)) then table.insert(result, v) end end return result end function p.uniqueBy(array, iteratee) return p._uniqueBy(iteratee)(array) end function p._uniqueBy(iteratee) local mapper = h.mapper(iteratee) return function(array) local result = {} for _, v in ipairs(array) do local match = p.find(result, function(val) return mapper(val) == mapper(v) end) if not match then table.insert(result, v) end end return result end end -- Based on https://github.com/lua-stdlib/functional -- See https://lua-stdlib.github.io/lua-stdlib/modules/std.functional.html#zip function p.zip(array, padValue) local result = {} local len = p.len(array) for outerk, innerTbl in ipairs(array) do innerTbl = p.padNils(innerTbl, "NIL") -- to properly handle sparse arrays for k, v in ipairs(innerTbl) do result[k] = result[k] or {} if v ~= "NIL" then result[k][outerk] = v end end if padValue then for k in ipairs(result) do result[k] = p.padNils(result[k], padValue, len) end end end return result end p.Schemas = function() return { print = { singleLine = { type = "boolean", } }, padNils = { padValue = { type = "any", default = '""', }, max = { type = "number", default = "table.maxn(array)", }, }, slice = { ["start"] = { type = "number", required = true, }, ["end"] = { type = "number", default = "#array", }, }, toArray = { keys = { type = "array", items = { oneOf = { { type = "string" }, { type = "number" }, }, }, }, padValue = { type = "any" }, }, union = { arrays = { required = true, type = "array", items = { type = "array", items = { type = "any", }, }, }, }, zip = { arrays = { type = "array", required = true, items = { type = "any" }, }, padValue = { type = "any", }, }, } end p.Documentation = function() local propertyShorthand = "<code>{{Sect|property}}</code> shorthand" local isMatchShorthand = "<code>{{Sect|isMatch}}</code> shorthand" return { sections = { { heading = "Tables", section = { clone = { params = {"tbl"}, returns = "Creates a shallow clone of <code>tbl</code>.", cases = { { snippet = 1, expect = { { foo = {} }, true, true }, }, }, }, cloneDeep = { desc = "Similar to {{Scribunto Manual|lib=mw.clone}} except that it differs in how it handles metatables.", params = {"tbl"}, returns = "Creates a recursive clone of <code>tbl</code>.", cases = { { snippet = 1, expect = { { foo = {} }, true, true, }, }, }, }, hasArray = { params = {"tbl"}, returns = "True if the table contains integer keys (or is empty).", cases = { { args = {{1, 2, 3}}, expect = true, }, { args = {{}}, expect = true, }, { args = {{foo = "bar"}}, expect = false, }, { args = {{foo = "bar", "baz"}}, expect = true, }, } }, hasKey = { params = {"tbl", "key"}, _params = {{"key"}, {"tbl"}}, returns = "True if <code>tbl[key]</code> is not nil, else false.", cases = { { args = {{"foo", nil, "bar"}, 3}, expect = true, }, { args = {{foo = "bar", baz = "quux"}, "baz"}, expect = true, }, { args = {{"foo", "bar"}, 3}, expect = false }, { args = {{foo = "a", baz = "b"}, "quux"}, expect = false, }, }, }, isArray = { params = {"tbl"}, returns = "True if the table contains '''only''' integer keys (or is empty).", cases = { { args = {{1, 2, 3}}, expect = true, }, { args = {{}}, expect = true, }, { args = {{nil, nil, "foo"}}, expect = true, }, { args = {{[10] = "foo"}}, expect = true, }, { args = {{foo = "bar"}}, expect = false, }, { args = {{foo = "bar", "baz"}}, expect = false, }, }, }, isEmpty = { params = {"tbl"}, returns = "True if <code>tbl</code> has no keys whatsoever", cases = { { args = {{}}, expect = true, }, { args = {{ foo = "bar" }}, expect = false, }, { args = {{nil, "foo"}}, expect = false, }, } }, isEqual = { params = {"tbl", "other"}, _params = {{"tbl"}, {"other"}}, returns = "<code>true</code> if <code>tbl</code> deep equals <code>other</code>.", cases = { { args = { { foo = { bar = "baz" } }, { foo = { bar = "baz" } }, }, expect = true, }, { args = { { foo = { bar = "baz" } }, { foo = { bar = "quux" } }, }, expect = false, }, } }, invert = { params = {"tbl"}, returns = "A table with the values of <code>tbl</code> as its keys, and vice-versa.", cases = { { args = {{"foo", "bar", baz = "quux"}}, expect = { foo = 1, bar = 2, quux = "baz" }, }, { desc = "Values will be overwritten if <code>tbl</code> has duplicate values. Overwrite order is not guaranteeed.", args = {{ Ganon = "Power", Ganondorf = "Power", Link = "Courage", Zelda = "Wisdom", }}, expect = { Power = "Ganondorf", Courage = "Link", Wisdom = "Zelda", }, }, }, }, isMatch = { desc = "Performs a partial deep comparison between <code>tbl</code> and <code>other</code> to determine if <code>tbl</code> contains equivalent values.", params = {"tbl", "source"}, _params = {{"source"}, {"tbl"}}, returns = "Returns <code>true</code> if <code>tbl</code> is a match, else false", cases = { { args = { { foo = { bar = "baz", flip = "flop" } }, { foo = { bar = "baz" } }, }, expect = true, }, { args = { {1, 2, 3}, {1, 2}, }, expect = true, }, { args = { { foo = { bar = "baz" } }, { foo = { bar = "quux" } }, }, expect = false }, }, }, keyOf = { params = {"tbl", "value"}, returns = "First key found whose value is shallow-equal to <code>value</code>, or nil if none found.", cases = { outputOnly = true, { args = {{"foo", nil, "bar"}, "bar"}, expect = 3, }, { args = {{foo = "bar", baz = "quux"}, "quux"}, expect = "baz", }, { args = {{"foo", "bar"}, "quux"}, expect = nil }, { desc = "Does not perform deep-equal checks on tables.", args = {{{}, {}}, {}}, expect = nil }, }, }, keys = { desc = "See also {{Sect|values}} and {{Sect|stringKeys}}.", params = {"tbl"}, returns = "Array of <code>tbl</code> keys.", cases = { { args = {{"foo", "bar", "baz"}}, expect = {1, 2, 3}, }, { args = {{ baz = "quux", foo = "bar" }}, expect = {"foo", "baz"}, }, { args = {{"foo", bar = "baz", nil, "quux"}}, expect = {1, 3, "bar"} }, }, }, mapValues = { params = {"tbl", "iteratee"}, returns = "Creates a table with the same keys as <code>tbl</code> and values generated by running each value of <code>tbl</code> thru iteratee.", cases = { { snippet = 1, expect = { arg1 = "foo", arg2 = "bar"} }, { desc = propertyShorthand, args = { { Link = { Triforce = "Courage", }, Zelda = { Triforce = "Wisdom" }, }, "Triforce", }, expect = { Link = "Courage", Zelda = "Wisdom", }, }, }, }, merge = { desc = "Recursively merges tables.", params = {"tbl", "..." }, returns = "<code>tbl</code> with merged values. Subsequent sources overwrite key assignments of previous sources.", cases = { { snippet = 1, expect = { flib = "flob", foo = { bar = {"noot", "flob"}, baz = "noot", wibble = "wobble", }, } }, { desc = "Common use: merging keys into new table.", args = { {}, { flib = "flob" }, { wibble = "wobble" }, }, expect = { flib = "flob", wibble = "wobble", }, }, } }, print = { params = {"tbl", "singleLine"}, returns = "<code>tbl</code> pretty-printed as a string.", cases = { outputOnly = true, { desc = "Prints array items on a single line.", args = {{"foo", "bar", "baz"}}, expect = '{"foo", "bar", "baz"}', }, { desc = "Prints single line when tables has one string key.", args = {{ foo = "bar" }}, expect = '{ foo = "bar" }', }, { args = {{1, 2, 3, foo = "bar" }}, expect = '{1, 2, 3, foo = "bar"}', }, { desc = "Prints one value per line if more than one string key.", args = {{ foo = "bar", flib = "flub" }}, expect = [[{ flib = "flub", foo = "bar", }]], }, { args = {{1, 2, 3, foo = "bar", flib = "flub" }}, expect = [[{ 1, 2, 3, flib = "flub", foo = "bar", }]] }, { desc = "Prints one value per line if any are tables.", args = {{{1}, {2}}}, expect = [[{ {1}, {2}, }]], }, { desc = string.format("Prints one value per line if single-line would exceed %s characters.", MAX_SINGLELINE), args = {{"abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"}}, expect = [[{ "abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz", }]] }, { desc = "Always prints single-line when <code>singleLine</code> is true", args = {{"abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"}, true}, expect = '{"abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"}' }, }, }, printPath = { params = {"path"}, returns = "Property path as a string.", cases = { outputOnly = true, { args = {{"foo", 1, "bar", 1}}, expect = ".foo[1].bar[1]", }, { args = {{"foo", '["bar"]'}}, expect = '.foo["bar"]', }, }, }, property = { params = {"tbl", "path"}, _params = {{"path"}, {"tbl"}}, returns = "The value at <code>path</code> for <code>tbl</code>.", cases = { outputOnly = true, { args = {{ foo = {"bar"} }, {"foo", 1}}, expect = "bar", }, { args = {{ foo = {"bar"} }, {"foo", "bar", "baz"}}, expect = nil, }, }, }, size = { params = {"tbl"}, returns = "Total number of keys in <code>tbl</code>.", cases = { { args = {{1, 2, foo = "bar"}}, expect = 3, }, }, }, stringKeys = { params = {"tbl"}, returns = "Array of string keys in <code>tbl</code>", cases = { { args = {{1, 2, foo = "bar", [true] = false}}, expect = {"foo"}, }, }, }, ivalues = { params = {"tbl"}, returns = "Array of contiguous, integer-keyed values in <code>tbl</code> starting from 1.", cases = { { args = {{"foo", "bar", wibble = "wobble", [10] = "baz"}}, expect = {"foo", "bar"}, }, }, }, toArray = { params = {"tbl", "keys", "padValue"}, _params = {{"keys", "padValue"}, {"tbl"}}, returns = "An array of all the values in <code>tbl</code>. Order not guaranteed unless <code>keys</code> is specified. If so, the returned array is such that <code>array[i] = utilsTable.keyOf(keys, tbl[i])</code>. Note that the function will return a sparse array when <code>tbl[i] == nil</code>.", cases = { { desc = "Order not guaranteed.", args = { { a = "foo", b = 2, c = true }, }, expect = {"foo", true, 2}, }, { desc = "<code>keys</code> param can be used to specify order.", args = { { a = "foo", b = 2, c = true }, {"c", "a", "b"}, }, expect = {true, "foo", 2}, }, { desc = "<code>keys</code> param can be used to grab a subset of the keys.", args = { { a = "foo", b = 2, c = true }, {"a", "b"}, }, expect = {"foo", 2}, }, { desc = "<code>keys</code> param can result in a sparse array if key not in <code>tbl</code>.", args = { { a = "foo", b = 2, c = true }, {"b", "a", "d", "c"}, }, expect = {2, "foo", [4] = true}, }, { desc = "Sparse arrays can be padded with a custom <code>padValue</code>.", args = { { a = "foo", b = 2, c = true }, {"b", "a", "d", "c"}, "" }, expect = {2, "foo", "", true}, }, }, }, }, }, { heading = "Arrays", section = { compact = { params = {"array"}, returns = "Creates an array with falsey values removed.", cases = { { args = {{0, false, 1, ""}}, expect = {0, 1, ""} }, }, }, concat = { params = {"array", "..."}, returns = "Creates a new array concatenating array with any additional arrays and/or values.", cases = { { snippet = 1, expect = {1, 2, 3, 4, {5}, 6}, }, { desc = "Tables with only string keys are treated as single values.", args = {{}, {1, 2}, {3, 4, foo = "bar"}, { foo = "quux" }}, expect = {1, 2, 3, 4, { foo = "quux" }}, }, }, }, difference = { params = {"array", "other"}, returns = "An array of all the elements in <code>array</code> that are not in <code>other</code>.", cases = { { args = {{"a", "b", "c"}, {"c", "d"}}, expect = {"a", "b"}, }, { desc = "Does not deep-compare.", args = {{{ foo = "bar" }}, {{ foo = "bar" }}}, expect = {{ foo = "bar" }}, } }, }, dropWhile = { params = {"array", "predicate"}, _params = {{"predicate"}, {"array"}}, returns = "Creates a slice of <code>array</code> excluding elements dropped from the beginning. Elements are dropped until <code>predicate</code> returns falsey.", cases = { { snippet = 1, expect = {"ALttP", "LA", "OoT"}, }, { desc = propertyShorthand, args = { { { game = "SS", hasMasterSword = true }, { game = "ALBW", hasMasterSword = true }, { game = "TFH", hasMasterSword = false }, { game = "BotW", hasMasterSword = true }, }, "hasMasterSword", }, expect = { { game = "TFH", hasMasterSword = false }, { game = "BotW", hasMasterSword = true }, }, }, { snippet = 2, expect = {}, }, }, }, dropRightWhile = { params = {"array", "predicate"}, _params = {{"predicate"}, {"array"}}, returns = "Creates a slice of <code>array</code> excluding elements dropped from the end. Elements are dropped until <code>predicate</code> returns falsey.", cases = { { snippet = 1, expect = {"...", "SS", "ALBW"}, }, { desc = propertyShorthand, args = { { { game = "SS", hasMasterSword = true }, { game = "ALBW", hasMasterSword = true }, { game = "TFH", hasMasterSword = false }, { game = "BotW", hasMasterSword = true }, }, "hasMasterSword", }, expect = { { game = "SS", hasMasterSword = true }, { game = "ALBW", hasMasterSword = true }, { game = "TFH", hasMasterSword = false }, }, }, { desc = isMatchShorthand, args = { { { game = "SS", system = "console" }, { game = "ALBW", system = "handheld" }, { game = "TFH", system = "handheld" }, { game = "BotW", system = "console" }, }, { system = "console" }, }, expect = { { game = "SS", system = "console" }, { game = "ALBW", system = "handheld" }, { game = "TFH", system = "handheld" }, }, }, }, }, keyBy = { params = {"array", "iteratee"}, _params = {{"iteratee"}, {"array"}}, returns = "Creates a table composed of keys generated from the results of running each element of <code>array</code> thru <code>iteratee</code>", cases = { { snippet = 1, expect = { ["TWW Link"] = { name = "Link", game = "TWW", age = 10, }, ["TP Link"] = { name = "Link", game = "TP", age = 17, }, }, }, { desc = propertyShorthand, args = {{ { name = "Link", age = 10, }, { name = "Zelda", age = 10, }, { name = "Zelda", age = 17, }, }, "name"}, expect = { ["Link"] = { name = "Link", age = 10 }, ["Zelda"] = { name = "Zelda", age = 17, }, } }, }, }, filter = { params = {"array", "iteratee"}, _params = {{"iteratee"}, {"array"}}, returns = "Iterates over array elements in <code>array</code>, returning an array of all elements <code>iteratee</code> returns truthy for.", cases = { { snippet = 1, expect = {"foo", "bar"}, }, { desc = propertyShorthand, args = { { { game = "The Wind Waker", canon = true }, { game = "Twilight Princess", canon = true }, { game = "Tingle's Rosy Rupeeland", canon = false }, }, "canon", }, expect = { { game = "The Wind Waker", canon = true }, { game = "Twilight Princess", canon = true }, }, }, { desc = isMatchShorthand, args = { { { game = "The Wind Waker", type = "main" }, { game = "Twilight Princess", type = "main" }, { game = "Tingle's Rosy Rupeeland", type = "spinoff" }, }, { type = "main" }, }, expect = { { game = "The Wind Waker", type = "main" }, { game = "Twilight Princess", type = "main" }, }, }, }, }, find = { params = {"tbl", "iteratee"}, _params = {{"iteratee"}, {"tbl"}}, returns = {"The value if found, else <code>nil</code>", "The index of the value found, else <code>nil</code>"}, cases = { outputOnly = true, { snippet = 1, expect = {"bar", 2} }, { snippet = 2, expect = {nil, nil}, }, { desc = propertyShorthand, args = { { { game = "Tingle's Rosy Rupeeland", canon = false }, { game = "The Wind Waker", canon = true }, { game = "Breath of the Wild", canon = true }, }, "canon" }, expect = { { game = "The Wind Waker", canon = true }, 2, }, }, { desc = isMatchShorthand, args = { { { game = "Tingle's Rosy Rupeeland", canon = false, type = "spinoff", }, { game = "Twilight Princess HD", canon = true, type = "remake", }, { game = "Breath of the Wild", canon = true, type = "main" }, }, { canon = true, type = "main" } }, expect = { { game = "Breath of the Wild", canon = true, type = "main" }, 3 } } } }, findIndex = { params = {"tbl", "iteratee"}, returns = "The index of the value if found, else <code>nil</code>", cases = { outputOnly = true, { snippet = 1, expect = 2 }, { snippet = 2, expect = nil, }, { desc = propertyShorthand, args = { { { game = "Tingle's Rosy Rupeeland", canon = false }, { game = "The Wind Waker", canon = true }, { game = "Breath of the Wild", canon = true }, }, "canon" }, expect = 2, }, { desc = isMatchShorthand, args = { { { game = "Tingle's Rosy Rupeeland", canon = false, type = "spinoff", }, { game = "Twilight Princess HD", canon = true, type = "remake", }, { game = "Breath of the Wild", canon = true, type = "main" }, }, { canon = true, type = "main" } }, expect = 3 } }, }, findLast = { params = {"array", "iteratee"}, _params = {{"iteratee"}, {"array"}}, returns = { "The last value found matching <code>iteratee</code>, else <code>nil</code>.", "The index of the value, else <code>nil</code>." }, cases = { outputOnly = true, { snippet = 1, expect = {"baz", 3}, }, { snippet = 2, expect = {nil, nil}, }, { desc = propertyShorthand, args = { { { game = "Tingle's Rosy Rupeeland", canon = false }, { game = "The Wind Waker", canon = true }, { game = "Breath of the Wild", canon = true }, }, "canon" }, expect = { { game = "Breath of the Wild", canon = true }, 3, }, }, { desc = isMatchShorthand, args = { { { game = "Tingle's Rosy Rupeeland", canon = false, type = "spinoff", }, { game = "Twilight Princess", canon = true, type = "main", }, { game = "Breath of the Wild", canon = true, type = "main" }, }, { canon = true, type = "main" } }, expect = { { game = "Breath of the Wild", canon = true, type = "main" }, 3, }, }, }, }, flatten = { params = {"array"}, returns = "Returns <code>array</code> but flattened a single level deep.", cases = { { args = {{1, {2, {3, 4}}, 5}}, expect = {1, 2, {3, 4}, 5} }, }, }, flatMap = { params = {"array", "iteratee"}, _params = {{"iteratee"}, {"array"}}, returns = "A flattened array of values created by running each element in <code>array</code> thru <code>iteratee</code> and flattening the mapped results.", cases = { { snippet = 1, expect = {1, 1, 2, 2}, }, }, }, fromPairs = { params = {"pairs"}, returns = "An object composed from key-value <code>pairs</code>.", cases = { { args = { { {"a", 1}, {"b", 2}, }, }, expect = { a = 1, b = 2, }, }, }, }, groupBy = { params = {"array", "iteratee"}, _params = {{"iteratee"}, {"array"}}, returns = "A table composed of keys generated from the results of running each element of <code>array</code> thru <code>iteratee</code>. The order of grouped values is determined by the order they occur in <code>array</code>. The corresponding value of each key is an array of elements responsible for generating the key. If a <code>nil</code> key is generated for a given value, that value will be absent from the resulting table.", cases = { { snippet = 1, expect = { [6] = {6.1, 6.3}, [4] = {4.2}, }, }, { desc = propertyShorthand, args = { { { page = "Link", term = "Hero of Time", game = "OoT", }, { page = "Link", term = "Hero of Winds", game = "TWW" }, { page = "Princess Zelda", term = "Zelda", game = "SS", }, { someOtherData = "foo", }, }, "page", }, expect = { ["Link"] = { { page = "Link", term = "Hero of Time", game = "OoT", }, { page = "Link", term = "Hero of Winds", game = "TWW" }, }, ["Princess Zelda"] = { { page = "Princess Zelda", term = "Zelda", game = "SS", }, }, }, }, }, }, includes = { params = {"array", "value"}, returns = "True if and only if <code>value</code> is in <code>array</code>.", cases = { { args = {{"foo", "bar"}, "foo"}, expect = true }, { args = {{"foo", "bar"}, "baz"}, expect = false }, }, }, intersection = { params = {"array", "other"}, returns = "An array of all the elements that are in both <code>array</code> and <code>other</code>", cases = { { args = {{"a", "b", "c"}, {"b"}}, expect = {"b"}, }, { desc = "Does not deep-compare.", args = {{{ foo = "bar" }}, {{ foo = "bar" }}}, expect = {}, }, }, }, len = { params = {"array"}, returns = "The length of the array. Works with {{Scribunto Manual|frame.args}} and tables loaded thru {{Scribunto Manual|mw.loadData}}.", cases = { { args = {{1, 2, 3, [10] = 10}}, expect = 3, }, }, }, map = { params = {"array", "iteratee"}, _params = {{"iteratee"}, {"array"}}, returns = "Creates an array of values by running each array element in <code>array</code> thru <code>iteratee</code>.", cases = { { snippet = 1, expect = {"true", "false"}, }, { desc = propertyShorthand, args = { { { name = "Link", triforce = "Courage", }, { name = "Zelda", triforce = "Wisdom", }, { name = "Ganon", triforce = "Power" }, }, "triforce" }, expect = {"Courage", "Wisdom", "Power"}, }, }, }, min = { params = {"array"}, returns = "The maximum value of <code>array</code>, or nil if empty.", cases = { { args = {{3, 2, 1}}, expect = 1, }, { args = {{}}, expect = nil, }, }, }, max = { params = {"array"}, returns = "The maximum value of <code>array</code>, or nil if empty.", cases = { { args = {{5, 10, 20}}, expect = 20, }, { args = {{}}, expect = nil, }, }, }, padNils = { params = {"array", "padValue", "max"}, _params = {{"padValue", "max"}, {"array"}}, returns = "Returns a version of <code>array</code> such that nil-valued indices up to and including <code>max</code> are replaced with <code>padValue</code>.", cases = { { args = {{nil, 2, nil, 4}, 0}, expect = {0, 2, 0, 4}, }, { args = {{nil, 2, nil, 4}, 0, 1}, expect = {0, 2, nil, 4}, }, { args = {{nil, 2, nil, 4}, 0, 6}, expect = {0, 2, 0, 4, 0, 0}, }, { args = {{"foo", nil, "baz"}}, expect = {"foo", "", "baz"}, }, }, }, partition = { params = {"array", "iteratee"}, _params = {{"iteratee"}, {"array"}}, returns = { "<code>array</code> elements that <code>iteratee</code> returns truthy for.", "<code>array</code> elements that <code>iteratee</code> returns falsey for.", }, cases = { outputOnly = true, { snippet = 1, expect = { {"foo", "quux"}, {"bar", "baz"}, }, }, { desc = propertyShorthand, args = { { { game = "The Wind Waker", canon = true }, { game = "Twilight Princess", canon = true }, { game = "Tingle's Rosy Rupeeland", canon = false }, }, "canon", }, expect = { { { game = "The Wind Waker", canon = true }, { game = "Twilight Princess", canon = true }, }, { { game = "Tingle's Rosy Rupeeland", canon = false }, } }, }, { desc = isMatchShorthand, args = { { { game = "The Wind Waker", type = "main" }, { game = "Twilight Princess", type = "main" }, { game = "Tingle's Rosy Rupeeland", type = "spinoff" }, }, { type = "main" }, }, expect = { { { game = "The Wind Waker", type = "main" }, { game = "Twilight Princess", type = "main" }, }, { { game = "Tingle's Rosy Rupeeland", type = "spinoff" }, }, }, }, }, }, reverse = { params = {"array"}, returns = "Reverses <code>array</code> so that the first element becomes the last, the second element becomes the second to last, and so on.", cases = { { args = {{1, 2, 3}}, expect = {3, 2, 1}, }, { desc = "Ignores non-consecutive integer keys", args = {{1, 2, 3, [100] = 100}}, expect = {3, 2, 1}, }, }, }, slice = { params = {"array", "start", "end"}, _params = {{"start", "end"}, {"array"}}, returns = "Creates a slice of <code>array</code> from <code>start</code> up to and including <code>end</code>.", cases = { { args = {{"a", "b", "c"}, 1, 2}, expect = {"a", "b"}, }, { args = {{"a", "b", "c"}, 2, nil}, expect = {"b", "c"}, }, } }, sortBy = { params = {"array", "iteratees"}, _params = {{"iteratees"}, {"array"}}, returns = "An array of elements, sorted in ascending order by the results of running each element in <code>array</code> thru each iteratee. This method performs an <u>unstable</u> sort, that is, it does not guarantee that the original order of equal elements will be preserved.", cases = { { snippet = 1, expect = { { game = "TWW", systems = {"GCN", "Wii U"}, }, { game = "LA", systems = {"GB", "GBC", "3DS VC"} }, { game = "OoT", systems = {"N64", "GCN", "iQue", "Wii VC", "Wii U VC"}, }, }, }, { desc = propertyShorthand, args = { { { name = "Link", age = 10, }, { name = "Zelda", age = 10 }, { name = "Zelda", age = 17 }, { name = "Link", age = 17 }, }, "name", }, expect = { { name = "Link", age = 10, }, { name = "Link", age = 17, }, { name = "Zelda", age = 17, }, { name = "Zelda", age = 10, }, }, }, { desc = "Multiple sort criteria", args = { { { name = "Link", age = 10, }, { name = "Zelda", age = 10 }, { name = "Zelda", age = 17 }, { name = "Link", age = 17 }, }, {"name", "age"}, }, expect = { { name = "Link", age = 10, }, { name = "Link", age = 17, }, { name = "Zelda", age = 10, }, { name = "Zelda", age = 17, }, }, }, }, }, tail = { params = {"array"}, returns = "Gets all but the first element of <code>array</code>.", cases = { { args = {{1, 2, 3}}, expect = {2, 3}, }, { args = {{}}, expect = {}, }, } }, take = { params = {"array", "n"}, _params = {{"n"}, {"array"}}, returns = "Creates a slice of array with n elements taken from the beginning.", cases = { { args = {{1, 2, 3}, 2}, expect = {1, 2}, }, { args = {{1, 2, 3}, 5}, expect = {1, 2, 3}, }, { args = {{1, 2, 3}, 0}, expect = {}, }, }, }, takeWhile = { params = {"array", "predicate"}, _params = {{"predicate"}, {"array"}}, returns = "Creates a slice of <code>array</code> with elements taken from the beginning. Elements are taken until <code>predicate</code> returns falsey.", cases = { { snippet = 1, expect = {"TLoZ", "TAoL", "ALttP", "LA"}, }, { desc = propertyShorthand, args = { { { game = "TLoZ", is2D = true }, { game = "TAoL", is2D = true }, { game = "ALttP", is2D = true }, { game = "LA", is2D = true }, { game = "OoT", is2D = false }, { game = "TMC", is2D = true }, }, "is2D", }, expect = { { game = "TLoZ", is2D = true }, { game = "TAoL", is2D = true }, { game = "ALttP", is2D = true }, { game = "LA", is2D = true }, }, }, { desc = isMatchShorthand, args = { { { game = "TLoZ", system = "console" }, { game = "TAoL", system = "console" }, { game = "ALttP", system = "console" }, { game = "LA", system = "handheld" }, { game = "OoT", system = "console" }, }, { system = "console" }, }, expect = { { game = "TLoZ", system = "console" }, { game = "TAoL", system = "console" }, { game = "ALttP", system = "console" }, }, }, }, }, union = { params = {"arrays"}, returns = "An array of unique values, in order, from all given arrays.", cases = { { args = {{{"a", "b", "c"}, {"b"}, {"c", "d"}}}, expect = {"a", "b", "c", "d"}, }, { desc = "Does not deep-compare for equality.", args = { { { { foo = "bar" }, { foo = "bar" }, }, } }, expect = { { foo = "bar" }, { foo = "bar" }, }, }, }, }, unique = { params = {"array"}, returns = "A copy of <code>array</code> but without the duplicate values. Elements are deep-compared. he order of result values is determined by the order they occur in the array.", cases = { { args = { {1, 2, 2, {foo = "bar"}, {foo = "quux"}, {foo = "bar"}}, }, expect = {1, 2, {foo = "bar"}, {foo = "quux"}}, }, }, }, uniqueBy = { params = {"array", "iteratee"}, _params = {{"iteratee"}, {"array"}}, returns = "A copy of <code>array</code> but without the duplicate values. <code>iteratee</code> is invoked for each element in array to generate the criterion by which uniqueness is computed.", cases = { { snippet = 1, expect = { { title = "LA", game = "LA" }, { title = "OoT", game = "OoT" }, }, }, { desc = propertyShorthand, args = { { { title = "LA", game = "LA" }, { title = "OoT", game = "OoT" }, { title = "LADX", game = "LA" }, { title = "OoT3D", game = "OoT" }, { title = "LANS", game = "LA" }, }, "game" }, expect = { { title = "LA", game = "LA" }, { title = "OoT", game = "OoT" }, }, }, }, }, zip = { params = {"arrays", "padValue"}, returns = [[ Creates a transposition of <code>arrays</code>. That is, an array of grouped elements, the first of which contains the first elements of the given arrays, the second of which contains the second elements of the given arrays, and so on. Should the elements of <code>arrays</code> not be of the same length, the resulting groups can be padded to the same length with <code>padValue</code>. ]], cases = { { args = {{ {1, 2}, {"a", "b"}, {true, false}, }}, expect = { {1, "a", true}, {2, "b", false}, }, }, { args = {{ {1, 2}, {"a", "b", "c"}, {true} }, ""}, expect = { {1, "a", true}, {2, "b", ""}, {"", "c", ""}, }, }, { args = {{ {1, 2}, {"a", "b", "c"}, {true} }}, expect = { {1, "a", true}, {2, "b", nil}, {nil, "c", nil}, } } }, }, }, }, }, } end return p