July 25, 2020

👭 Knight Challenge #11 👬

Want to try your hand at these challenges? There's a couple of things you can do!
From writing, to research, to images, find your preferred way to contribute with our eleventh theme: Couples!

Latest Announcements

Module:UtilsTable

From Zelda Wiki, the Zelda encyclopedia
Jump to: navigation, search

Utility library for operating on Lua tables. Inspired by Lodash.

Note that the array functions in this module are not designed to process "sparse arrays" such as {1, nil, 3} or {nil, 2, 3}. The only exceptions are compact and padNils, which transform sparse arrays into "dense" ones.

Some functions have two variants: a "normal" procedural programming variant, and a functional programming variant of the same name but prefixed with _. The latter functions are curried and iteratee-first data-last as in lodash/fp. This is useful for composition. For example,

Input Function Output
local games = {
  {
    initialism = "TMC",
    subtitle = "The Minish Cap",
  },
  {
    initialism = "TWW",
    subtitle = "The Wind Waker",
  },
  {
    initialism = "TP",
    subtitle = "Twilight Princess",
  },
}
Without UtilsWith UtilsWith Utils (Functional)
local result = {}
for _, game in ipairs(games) do
  if string.sub(game.subtitle, 1, 3) == "The" then
    table.insert(result, game.subtitle)
  end
end
return result
local result = games
result = utilsTable.map(result, "subtitle")
result = utilsTable.filter(result, utilsString._startsWith("The")) 
return result
return utilsFunction.pipe(games) {
  utilsTable._map("subtitle"),
  utilsTable._filter(utilsString._startsWith("The")),
}
{"The Minish Cap", "The Wind Waker"}

The above example is somewhat contrived for the sake of simplicity. In real contexts, use whichever utility function results in the greatest readability. Sometimes the best option will be to forgo these utilities in favor of plain for-loops.


Tables

clone

clone(tbl)

Returns
  • Creates a shallow clone of tbl.
Examples
InputOutputStatus
local original = { foo = {} }
local clone = utilsTable.clone(original)
return {
  clone,
  original ~= clone, 
  original.foo == clone.foo,
}
{
  {
    foo = {},
  },
  true,
  true,
}
Green check.svg

cloneDeep

cloneDeep(tbl)

Similar to mw.clone except that it differs in how it handles metatables.

Returns
  • Creates a recursive clone of tbl.
Examples
InputOutputStatus
local original = { foo = {} }
local clone = utilsTable.cloneDeep(original)
return {
  clone,
  original ~= clone, 
  original.foo ~= clone.foo,
}
{
  {
    foo = {},
  },
  true,
  true,
}
Green check.svg

hasArray

hasArray(tbl)

Returns
  • True if the table contains integer keys (or is empty).
Examples
InputOutputStatus
hasArray({1, 2, 3})
true
Green check.svg
hasArray({})
true
Green check.svg
hasArray({ foo = "bar" })
false
Green check.svg
hasArray({"baz", foo = "bar"})
true
Green check.svg

hasKey

hasKey_hasKey
hasKey(tbl, key)
Returns
  • True if tbl[key] is not nil, else false.
Examples
InputOutputStatus
hasKey({"foo", [3] = "bar"}, 3)
true
Green check.svg
hasKey(
  {
    baz = "quux",
    foo = "bar",
  },
  "baz"
)
true
Green check.svg
hasKey({"foo", "bar"}, 3)
false
Green check.svg
hasKey(
  {
    baz = "b",
    foo = "a",
  },
  "quux"
)
false
Green check.svg
_hasKey(key)(tbl)
Returns
  • True if tbl[key] is not nil, else false.
Examples
InputOutputStatus
_hasKey(3)({"foo", [3] = "bar"})
true
Green check.svg
_hasKey("baz")({
  foo = "bar",
  baz = "quux",
})
true
Green check.svg
_hasKey(3)({"foo", "bar"})
false
Green check.svg
_hasKey("quux")({
  foo = "a",
  baz = "b",
})
false
Green check.svg

invert

invert(tbl)

Returns
  • A table with the values of tbl as its keys, and vice-versa.
Examples
InputOutputStatus
invert({"foo", "bar", baz = "quux"})
{
  quux = "baz",
  bar = 2,
  foo = 1,
}
Green check.svg
Values will be overwritten if tbl has duplicate values. Overwrite order is not guaranteeed.
invert({
  Ganon = "Power",
  Link = "Courage",
  Zelda = "Wisdom",
  Ganondorf = "Power",
})
{
  Wisdom = "Zelda",
  Courage = "Link",
  Power = "Ganondorf",
}
Green check.svg

isArray

isArray(tbl)

Returns
  • True if the table contains only integer keys (or is empty).
Examples
InputOutputStatus
isArray({1, 2, 3})
true
Green check.svg
isArray({})
true
Green check.svg
isArray({[3] = "foo" })
true
Green check.svg
isArray({ [10] = "foo" })
true
Green check.svg
isArray({ foo = "bar" })
false
Green check.svg
isArray({"baz", foo = "bar"})
false
Green check.svg

isEmpty

isEmpty(tbl)

Returns
  • True if tbl has no keys whatsoever
Examples
InputOutputStatus
isEmpty({})
true
Green check.svg
isEmpty({ foo = "bar" })
false
Green check.svg
isEmpty({[2] = "foo" })
false
Green check.svg

isEqual

isEqual_isEqual
isEqual(tbl, other)
Returns
  • true if tbl deep equals other.
Examples
InputOutputStatus
isEqual(
  {
    foo = { bar = "baz" },
  },
  {
    foo = { bar = "baz" },
  }
)
true
Green check.svg
isEqual(
  {
    foo = { bar = "baz" },
  },
  {
    foo = { bar = "quux" },
  }
)
false
Green check.svg
_isEqual(tbl)(other)
Returns
  • true if tbl deep equals other.
Examples
InputOutputStatus
_isEqual({
  foo = { bar = "baz" },
})({
  foo = { bar = "baz" },
})
true
Green check.svg
_isEqual({
  foo = { bar = "baz" },
})({
  foo = { bar = "quux" },
})
false
Green check.svg

isMatch

isMatch_isMatch
isMatch(tbl, source)

Performs a partial deep comparison between tbl and other to determine if tbl contains equivalent values.

Returns
  • Returns true if tbl is a match, else false
Examples
InputOutputStatus
isMatch(
  {
    foo = {
      flip = "flop",
      bar = "baz",
    },
  },
  {
    foo = { bar = "baz" },
  }
)
true
Green check.svg
isMatch({1, 2, 3}, {1, 2})
true
Green check.svg
isMatch(
  {
    foo = { bar = "baz" },
  },
  {
    foo = { bar = "quux" },
  }
)
false
Green check.svg
_isMatch(source)(tbl)

Performs a partial deep comparison between tbl and other to determine if tbl contains equivalent values.

Returns
  • Returns true if tbl is a match, else false
Examples
InputOutputStatus
_isMatch({
  foo = { bar = "baz" },
})({
  foo = {
    bar = "baz",
    flip = "flop",
  },
})
true
Green check.svg
_isMatch({1, 2})({1, 2, 3})
true
Green check.svg
_isMatch({
  foo = { bar = "quux" },
})({
  foo = { bar = "baz" },
})
false
Green check.svg

ivalues

ivalues(tbl)

Returns
  • Array of contiguous, integer-keyed values in tbl starting from 1.
Examples
InputOutputStatus
ivalues({
  "foo",
  "bar",
  [10] = "baz",
  wibble = "wobble",
})
{"foo", "bar"}
Green check.svg

keyOf

keyOf(tbl, value)

Returns
  • First key found whose value is shallow-equal to value, or nil if none found.
Examples
InputOutputStatus
keyOf({"foo", [3] = "bar"}, "bar")
3
Green check.svg
keyOf(
  {
    baz = "quux",
    foo = "bar",
  },
  "quux"
)
"baz"
Green check.svg
keyOf({"foo", "bar"}, "quux")
nil
Green check.svg
Does not perform deep-equal checks on tables.
keyOf(
  {
    {},
    {},
  },
  {}
)
nil
Green check.svg

keys

keys(tbl)

See also values and stringKeys.

Returns
  • Array of tbl keys.
Examples
InputOutputStatus
keys({"foo", "bar", "baz"})
{1, 2, 3}
Green check.svg
keys({
  foo = "bar",
  baz = "quux",
})
{"foo", "baz"}
Green check.svg
keys({"foo", [3] = "quux", bar = "baz"})
{1, 3, "bar"}
Green check.svg

mapValues

mapValues(tbl, iteratee)

Returns
  • Creates a table with the same keys as tbl and values generated by running each value of tbl thru iteratee.
Examples
InputOutputStatus
local templateArgs = {
  arg1 = "  foo ",
  arg2 = "bar\n",
}
return utilsTable.mapValues(templateArgs, mw.text.trim)
{
  arg2 = "bar",
  arg1 = "foo",
}
Green check.svg
property shorthand
mapValues(
  {
    Zelda = { Triforce = "Wisdom" },
    Link = { Triforce = "Courage" },
  },
  "Triforce"
)
{
  Link = "Courage",
  Zelda = "Wisdom",
}
Green check.svg

merge

merge(tbl, ...)

Recursively merges tables.

Returns
  • tbl with merged values. Subsequent sources overwrite key assignments of previous sources.
Examples
InputOutputStatus
local tbl = {
  flib = "flob",
  foo = {
    bar = {"flib", "flob"},
    baz = {"quux", "quuz"},
  },
}
utilsTable.merge(tbl, {
  foo = {
    bar = {"noot"},
    baz = "noot",
    wibble = "wobble",
  },
})
return tbl
{
  flib = "flob",
  foo = {
    wibble = "wobble",
    baz = "noot",
    bar = {"noot", "flob"},
  },
}
Green check.svg
Common use: merging keys into new table.
merge({}, { flib = "flob" }, { wibble = "wobble" })
{
  flib = "flob",
  wibble = "wobble",
}
Green check.svg

print

print(tbl, [singleLine])

Parameters
Returns
  • tbl pretty-printed as a string.
Examples
InputOutputStatus
Prints array items on a single line.
print({"foo", "bar", "baz"})
'{"foo", "bar", "baz"}'
Green check.svg
Prints single line when tables has one string key.
print({ foo = "bar" })
'{ foo = "bar" }'
Green check.svg
print({1, 2, 3, foo = "bar"})
'{1, 2, 3, foo = "bar"}'
Green check.svg
Prints one value per line if more than one string key.
print({
  flib = "flub",
  foo = "bar",
})
'{\n  flib = "flub",\n  foo = "bar",\n}'
Green check.svg
print({
  1,
  2,
  3,
  flib = "flub",
  foo = "bar",
})
'{\n  1,\n  2,\n  3,\n  flib = "flub",\n  foo = "bar",\n}'
Green check.svg
Prints one value per line if any are tables.
print({
  {1},
  {2},
})
"{\n  {1},\n  {2},\n}"
Green check.svg
Prints one value per line if single-line would exceed 50 characters.
print({
  "abcdefghijklmnopqrstuvwxyz",
  "abcdefghijklmnopqrstuvwxyz",
})
'{\n  "abcdefghijklmnopqrstuvwxyz",\n  "abcdefghijklmnopqrstuvwxyz",\n}'
Green check.svg
Always prints single-line when singleLine is true
print(
  {
    "abcdefghijklmnopqrstuvwxyz",
    "abcdefghijklmnopqrstuvwxyz",
  },
  true
)
'{"abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"}'
Green check.svg

printPath

printPath(path)

Returns
  • Property path as a string.
Examples
InputOutputStatus
printPath({"foo", 1, "bar", 1})
".foo[1].bar[1]"
Green check.svg
printPath({"foo", '["bar"]'})
'.foo["bar"]'
Green check.svg

property

property_property
property(tbl, path)
Returns
  • The value at path for tbl.
Examples
InputOutputStatus
property(
  {
    foo = {"bar"},
  },
  {"foo", 1}
)
"bar"
Green check.svg
property(
  {
    foo = {"bar"},
  },
  {"foo", "bar", "baz"}
)
nil
Green check.svg
_property(path)(tbl)
Returns
  • The value at path for tbl.
Examples
InputOutputStatus
_property({"foo", 1})({
  foo = {"bar"},
})
"bar"
Green check.svg
_property({"foo", "bar", "baz"})({
  foo = {"bar"},
})
nil
Green check.svg

size

size(tbl)

Returns
  • Total number of keys in tbl.
Examples
InputOutputStatus
size({1, 2, foo = "bar"})
3
Green check.svg

stringKeys

stringKeys(tbl)

Returns
  • Array of string keys in tbl
Examples
InputOutputStatus
stringKeys({
  1,
  2,
  [true] = false,
  foo = "bar",
})
{"foo"}
Green check.svg

toArray

toArray_toArray
toArray(tbl, [keys], [padValue])
Parameters
Returns
  • An array of all the values in tbl. Order not guaranteed unless keys is specified. If so, the returned array is such that array[i] = utilsTable.keyOf(keys, tbl[i]). Note that the function will return a sparse array when tbl[i] == nil.
Examples
InputOutputStatus
Order not guaranteed.
toArray({
  a = "foo",
  c = true,
  b = 2,
})
{"foo", true, 2}
Green check.svg
keys param can be used to specify order.
toArray(
  {
    a = "foo",
    c = true,
    b = 2,
  },
  {"c", "a", "b"}
)
{true, "foo", 2}
Green check.svg
keys param can be used to grab a subset of the keys.
toArray(
  {
    a = "foo",
    c = true,
    b = 2,
  },
  {"a", "b"}
)
{"foo", 2}
Green check.svg
keys param can result in a sparse array if key not in tbl.
toArray(
  {
    a = "foo",
    c = true,
    b = 2,
  },
  {"b", "a", "d", "c"}
)
{2, "foo", [4] = true}
Green check.svg
Sparse arrays can be padded with a custom padValue.
toArray(
  {
    a = "foo",
    c = true,
    b = 2,
  },
  {"b", "a", "d", "c"},
  ""
)
{2, "foo", "", true}
Green check.svg
_toArray([keys], [padValue])(tbl)
Parameters
Returns
  • An array of all the values in tbl. Order not guaranteed unless keys is specified. If so, the returned array is such that array[i] = utilsTable.keyOf(keys, tbl[i]). Note that the function will return a sparse array when tbl[i] == nil.
Examples
InputOutputStatus
Order not guaranteed.
_toArray(nil)({
  a = "foo",
  c = true,
  b = 2,
})
{"foo", true, 2}
Green check.svg
keys param can be used to specify order.
_toArray({"c", "a", "b"})({
  a = "foo",
  c = true,
  b = 2,
})
{true, "foo", 2}
Green check.svg
keys param can be used to grab a subset of the keys.
_toArray({"a", "b"})({
  a = "foo",
  c = true,
  b = 2,
})
{"foo", 2}
Green check.svg
keys param can result in a sparse array if key not in tbl.
_toArray({"b", "a", "d", "c"})({
  a = "foo",
  c = true,
  b = 2,
})
{2, "foo", [4] = true}
Green check.svg
Sparse arrays can be padded with a custom padValue.
_toArray({"b", "a", "d", "c"}, "")({
  a = "foo",
  c = true,
  b = 2,
})
{2, "foo", "", true}
Green check.svg

Arrays

compact

compact(array)

Returns
  • Creates an array with falsey values removed.
Examples
InputOutputStatus
compact({0, false, 1, ""})
{0, 1, ""}
Green check.svg

concat

concat(array, ...)

Returns
  • Creates a new array concatenating array with any additional arrays and/or values.
Examples
InputOutputStatus
return utilsTable.concat({1}, 2, {3, 4}, {{5}}, {6})
{
  1,
  2,
  3,
  4,
  {5},
  6,
}
Green check.svg
Tables with only string keys are treated as single values.
concat({}, {1, 2}, {3, 4, foo = "bar"}, { foo = "quux" })
{
  1,
  2,
  3,
  4,
  { foo = "quux" },
}
Green check.svg

difference

difference(array, other)

Returns
  • An array of all the elements in array that are not in other.
Examples
InputOutputStatus
difference({"a", "b", "c"}, {"c", "d"})
{"a", "b"}
Green check.svg
Does not deep-compare.
difference(
  {
    { foo = "bar" },
  },
  {
    { foo = "bar" },
  }
)
{
  { foo = "bar" },
}
Green check.svg

dropRightWhile

dropRightWhile_dropRightWhile
dropRightWhile(array, predicate)
Returns
  • Creates a slice of array excluding elements dropped from the end. Elements are dropped until predicate returns falsey.
Examples
InputOutputStatus
local games = {"...", "SS", "ALBW", "TFH", "BotW"}
return utilsTable.dropRightWhile(games, function(game)
  return game ~= "ALBW"
end)
{"...", "SS", "ALBW"}
Green check.svg
property shorthand
dropRightWhile(
  {
    {
      hasMasterSword = true,
      game = "SS",
    },
    {
      hasMasterSword = true,
      game = "ALBW",
    },
    {
      hasMasterSword = false,
      game = "TFH",
    },
    {
      hasMasterSword = true,
      game = "BotW",
    },
  },
  "hasMasterSword"
)
{
  {
    hasMasterSword = true,
    game = "SS",
  },
  {
    hasMasterSword = true,
    game = "ALBW",
  },
  {
    hasMasterSword = false,
    game = "TFH",
  },
}
Green check.svg
isMatch shorthand
dropRightWhile(
  {
    {
      system = "console",
      game = "SS",
    },
    {
      system = "handheld",
      game = "ALBW",
    },
    {
      system = "handheld",
      game = "TFH",
    },
    {
      system = "console",
      game = "BotW",
    },
  },
  { system = "console" }
)
{
  {
    system = "console",
    game = "SS",
  },
  {
    system = "handheld",
    game = "ALBW",
  },
  {
    system = "handheld",
    game = "TFH",
  },
}
Green check.svg
_dropRightWhile(predicate)(array)
Returns
  • Creates a slice of array excluding elements dropped from the end. Elements are dropped until predicate returns falsey.
Examples
InputOutputStatus
property shorthand
_dropRightWhile("hasMasterSword")({
  {
    hasMasterSword = true,
    game = "SS",
  },
  {
    hasMasterSword = true,
    game = "ALBW",
  },
  {
    hasMasterSword = false,
    game = "TFH",
  },
  {
    hasMasterSword = true,
    game = "BotW",
  },
})
{
  {
    hasMasterSword = true,
    game = "SS",
  },
  {
    hasMasterSword = true,
    game = "ALBW",
  },
  {
    hasMasterSword = false,
    game = "TFH",
  },
}
Green check.svg
isMatch shorthand
_dropRightWhile({ system = "console" })({
  {
    game = "SS",
    system = "console",
  },
  {
    game = "ALBW",
    system = "handheld",
  },
  {
    game = "TFH",
    system = "handheld",
  },
  {
    game = "BotW",
    system = "console",
  },
})
{
  {
    game = "SS",
    system = "console",
  },
  {
    game = "ALBW",
    system = "handheld",
  },
  {
    game = "TFH",
    system = "handheld",
  },
}
Green check.svg

filter

filter_filter
filter(array, iteratee)
Returns
  • Iterates over array elements in array, returning an array of all elements iteratee returns truthy for.
Examples
InputOutputStatus
local args = {"foo", "", "bar"}
return utilsTable.filter(args, utilsString.notEmpty)
{"foo", "bar"}
Green check.svg
property shorthand
filter(
  {
    {
      canon = true,
      game = "The Wind Waker",
    },
    {
      canon = true,
      game = "Twilight Princess",
    },
    {
      canon = false,
      game = "Tingle's Rosy Rupeeland",
    },
  },
  "canon"
)
{
  {
    canon = true,
    game = "The Wind Waker",
  },
  {
    canon = true,
    game = "Twilight Princess",
  },
}
Green check.svg
isMatch shorthand
filter(
  {
    {
      type = "main",
      game = "The Wind Waker",
    },
    {
      type = "main",
      game = "Twilight Princess",
    },
    {
      type = "spinoff",
      game = "Tingle's Rosy Rupeeland",
    },
  },
  { type = "main" }
)
{
  {
    type = "main",
    game = "The Wind Waker",
  },
  {
    type = "main",
    game = "Twilight Princess",
  },
}
Green check.svg
_filter(iteratee)(array)
Returns
  • Iterates over array elements in array, returning an array of all elements iteratee returns truthy for.
Examples
InputOutputStatus
property shorthand
_filter("canon")({
  {
    canon = true,
    game = "The Wind Waker",
  },
  {
    canon = true,
    game = "Twilight Princess",
  },
  {
    canon = false,
    game = "Tingle's Rosy Rupeeland",
  },
})
{
  {
    canon = true,
    game = "The Wind Waker",
  },
  {
    canon = true,
    game = "Twilight Princess",
  },
}
Green check.svg
isMatch shorthand
_filter({ type = "main" })({
  {
    game = "The Wind Waker",
    type = "main",
  },
  {
    game = "Twilight Princess",
    type = "main",
  },
  {
    game = "Tingle's Rosy Rupeeland",
    type = "spinoff",
  },
})
{
  {
    game = "The Wind Waker",
    type = "main",
  },
  {
    game = "Twilight Princess",
    type = "main",
  },
}
Green check.svg

find

find_find
find(tbl, iteratee)
Returns
  • The value if found, else nil
  • The index of the value found, else nil
Examples
InputOutputStatus
local args = {"foo", "bar", "baz"}
return utilsTable.find(args, utilsString._startsWith("b"))
"bar"
Green check.svg
2
Green check.svg
local args = {"foo", "quux", "quux"}
return utilsTable.find(args, utilsString._startsWith("b"))
nil
Green check.svg
nil
Green check.svg
property shorthand
find(
  {
    {
      canon = false,
      game = "Tingle's Rosy Rupeeland",
    },
    {
      canon = true,
      game = "The Wind Waker",
    },
    {
      canon = true,
      game = "Breath of the Wild",
    },
  },
  "canon"
)
{
  canon = true,
  game = "The Wind Waker",
}
Green check.svg
2
Green check.svg
isMatch shorthand
find(
  {
    {
      canon = false,
      game = "Tingle's Rosy Rupeeland",
      type = "spinoff",
    },
    {
      canon = true,
      game = "Twilight Princess HD",
      type = "remake",
    },
    {
      canon = true,
      game = "Breath of the Wild",
      type = "main",
    },
  },
  {
    canon = true,
    type = "main",
  }
)
{
  canon = true,
  game = "Breath of the Wild",
  type = "main",
}
Green check.svg
3
Green check.svg
_find(iteratee)(tbl)
Returns
  • The value if found, else nil
  • The index of the value found, else nil
Examples
InputOutputStatus
property shorthand
_find("canon")({
  {
    canon = false,
    game = "Tingle's Rosy Rupeeland",
  },
  {
    canon = true,
    game = "The Wind Waker",
  },
  {
    canon = true,
    game = "Breath of the Wild",
  },
})
{
  canon = true,
  game = "The Wind Waker",
}
Green check.svg
2
Green check.svg
isMatch shorthand
_find({
  canon = true,
  type = "main",
})({
  {
    canon = false,
    game = "Tingle's Rosy Rupeeland",
    type = "spinoff",
  },
  {
    canon = true,
    game = "Twilight Princess HD",
    type = "remake",
  },
  {
    canon = true,
    game = "Breath of the Wild",
    type = "main",
  },
})
{
  canon = true,
  game = "Breath of the Wild",
  type = "main",
}
Green check.svg
3
Green check.svg

findIndex

findIndex(tbl, iteratee)

Returns
  • The index of the value if found, else nil
Examples
InputOutputStatus
local args = {"foo", "bar", "baz"}
return utilsTable.findIndex(args, utilsString._startsWith("b"))
2
Green check.svg
local args = {"foo", "quux", "quux"}
return utilsTable.findIndex(args, utilsString._startsWith("b"))
nil
Green check.svg
property shorthand
findIndex(
  {
    {
      canon = false,
      game = "Tingle's Rosy Rupeeland",
    },
    {
      canon = true,
      game = "The Wind Waker",
    },
    {
      canon = true,
      game = "Breath of the Wild",
    },
  },
  "canon"
)
2
Green check.svg
isMatch shorthand
findIndex(
  {
    {
      canon = false,
      game = "Tingle's Rosy Rupeeland",
      type = "spinoff",
    },
    {
      canon = true,
      game = "Twilight Princess HD",
      type = "remake",
    },
    {
      canon = true,
      game = "Breath of the Wild",
      type = "main",
    },
  },
  {
    canon = true,
    type = "main",
  }
)
3
Green check.svg

findLast

findLast_findLast
findLast(array, iteratee)
Returns
  • The last value found matching iteratee, else nil.
  • The index of the value, else nil.
Examples
InputOutputStatus
local args = {"foo", "bar", "baz"}
return utilsTable.findLast(args, utilsString._startsWith("b"))
"baz"
Green check.svg
3
Green check.svg
local args = {"foo", "quux", "quux"}
return utilsTable.findLast(args, utilsString._startsWith("b"))
nil
Green check.svg
nil
Green check.svg
property shorthand
findLast(
  {
    {
      canon = false,
      game = "Tingle's Rosy Rupeeland",
    },
    {
      canon = true,
      game = "The Wind Waker",
    },
    {
      canon = true,
      game = "Breath of the Wild",
    },
  },
  "canon"
)
{
  canon = true,
  game = "Breath of the Wild",
}
Green check.svg
3
Green check.svg
isMatch shorthand
findLast(
  {
    {
      canon = false,
      game = "Tingle's Rosy Rupeeland",
      type = "spinoff",
    },
    {
      canon = true,
      game = "Twilight Princess",
      type = "main",
    },
    {
      canon = true,
      game = "Breath of the Wild",
      type = "main",
    },
  },
  {
    canon = true,
    type = "main",
  }
)
{
  canon = true,
  game = "Breath of the Wild",
  type = "main",
}
Green check.svg
3
Green check.svg
_findLast(iteratee)(array)
Returns
  • The last value found matching iteratee, else nil.
  • The index of the value, else nil.
Examples
InputOutputStatus
property shorthand
_findLast("canon")({
  {
    canon = false,
    game = "Tingle's Rosy Rupeeland",
  },
  {
    canon = true,
    game = "The Wind Waker",
  },
  {
    canon = true,
    game = "Breath of the Wild",
  },
})
{
  canon = true,
  game = "Breath of the Wild",
}
Green check.svg
3
Green check.svg
isMatch shorthand
_findLast({
  canon = true,
  type = "main",
})({
  {
    canon = false,
    game = "Tingle's Rosy Rupeeland",
    type = "spinoff",
  },
  {
    canon = true,
    game = "Twilight Princess",
    type = "main",
  },
  {
    canon = true,
    game = "Breath of the Wild",
    type = "main",
  },
})
{
  canon = true,
  game = "Breath of the Wild",
  type = "main",
}
Green check.svg
3
Green check.svg

flatten

flatten(array)

Returns
  • Returns array but flattened a single level deep.
Examples
InputOutputStatus
flatten({
  1,
  {
    2,
    {3, 4},
  },
  5,
})
{
  1,
  2,
  {3, 4},
  5,
}
Green check.svg

flatMap

flatMap_flatMap
flatMap(array, iteratee)
Returns
  • A flattened array of values created by running each element in array thru iteratee and flattening the mapped results.
Examples
InputOutputStatus
local function duplicate(n)
  return {n, n}
end
return utilsTable.flatMap({1, 2}, duplicate)
{1, 1, 2, 2}
Green check.svg
_flatMap(iteratee)(array)
Returns
  • A flattened array of values created by running each element in array thru iteratee and flattening the mapped results.
Examples
InputOutputStatus
local duplicate = utilsTable._flatMap(function(n)
  return {n, n}
end)
return duplicate({1, 2})
{1, 1, 2, 2}
Green check.svg

groupBy

groupBy_groupBy
groupBy(array, iteratee)
Returns
  • A table composed of keys generated from the results of running each element of array thru iteratee. The order of grouped values is determined by the order they occur in array. The corresponding value of each key is an array of elements responsible for generating the key. If a nil key is generated for a given value, that value will be absent from the resulting table.
Examples
InputOutputStatus
return utilsTable.groupBy({6.1, 4.2, 6.3}, math.floor)
{
  [6] = {6.1, 6.3},
  [4] = {4.2},
}
Green check.svg
property shorthand
groupBy(
  {
    {
      term = "Hero of Time",
      game = "OoT",
      page = "Link",
    },
    {
      term = "Hero of Winds",
      game = "TWW",
      page = "Link",
    },
    {
      term = "Zelda",
      game = "SS",
      page = "Princess Zelda",
    },
    { someOtherData = "foo" },
  },
  "page"
)
{
  ["Princess Zelda"] = {
    {
      term = "Zelda",
      game = "SS",
      page = "Princess Zelda",
    },
  },
  Link = {
    {
      term = "Hero of Time",
      game = "OoT",
      page = "Link",
    },
    {
      term = "Hero of Winds",
      game = "TWW",
      page = "Link",
    },
  },
}
Green check.svg
_groupBy(iteratee)(array)
Returns
  • A table composed of keys generated from the results of running each element of array thru iteratee. The order of grouped values is determined by the order they occur in array. The corresponding value of each key is an array of elements responsible for generating the key. If a nil key is generated for a given value, that value will be absent from the resulting table.
Examples
InputOutputStatus
property shorthand
_groupBy("page")({
  {
    term = "Hero of Time",
    game = "OoT",
    page = "Link",
  },
  {
    term = "Hero of Winds",
    game = "TWW",
    page = "Link",
  },
  {
    term = "Zelda",
    game = "SS",
    page = "Princess Zelda",
  },
  { someOtherData = "foo" },
})
{
  ["Princess Zelda"] = {
    {
      term = "Zelda",
      game = "SS",
      page = "Princess Zelda",
    },
  },
  Link = {
    {
      term = "Hero of Time",
      game = "OoT",
      page = "Link",
    },
    {
      term = "Hero of Winds",
      game = "TWW",
      page = "Link",
    },
  },
}
Green check.svg

includes

includes(array, value)

Returns
  • True if and only if value is in array.
Examples
InputOutputStatus
includes({"foo", "bar"}, "foo")
true
Green check.svg
includes({"foo", "bar"}, "baz")
false
Green check.svg

intersection

intersection(array, other)

Returns
  • An array of all the elements that are in both array and other
Examples
InputOutputStatus
intersection({"a", "b", "c"}, {"b"})
{"b"}
Green check.svg
Does not deep-compare.
intersection(
  {
    { foo = "bar" },
  },
  {
    { foo = "bar" },
  }
)
{}
Green check.svg

keyBy

keyBy_keyBy
keyBy(array, iteratee)
Returns
  • Creates a table composed of keys generated from the results of running each element of array thru iteratee
Examples
InputOutputStatus
local characters = {
  {
    name = "Link",
    game = "TWW",
    age = 10,
  },
  {
    name = "Link",
    game = "TP",
    age = 17
  },
}
return utilsTable.keyBy(characters, function(character)
  return character.game .. " " .. character.name
end)
{
  ["TP Link"] = {
    game = "TP",
    name = "Link",
    age = 17,
  },
  ["TWW Link"] = {
    game = "TWW",
    name = "Link",
    age = 10,
  },
}
Green check.svg
property shorthand
keyBy(
  {
    {
      name = "Link",
      age = 10,
    },
    {
      name = "Zelda",
      age = 10,
    },
    {
      name = "Zelda",
      age = 17,
    },
  },
  "name"
)
{
  Zelda = {
    name = "Zelda",
    age = 17,
  },
  Link = {
    name = "Link",
    age = 10,
  },
}
Green check.svg
_keyBy(iteratee)(array)
Returns
  • Creates a table composed of keys generated from the results of running each element of array thru iteratee
Examples
InputOutputStatus
property shorthand
_keyBy("name")({
  {
    name = "Link",
    age = 10,
  },
  {
    name = "Zelda",
    age = 10,
  },
  {
    name = "Zelda",
    age = 17,
  },
})
{
  Zelda = {
    name = "Zelda",
    age = 17,
  },
  Link = {
    name = "Link",
    age = 10,
  },
}
Green check.svg

len

len(array)

Returns
Examples
InputOutputStatus
len({1, 2, 3, [10] = 10})
3
Green check.svg

map

map_map
map(array, iteratee)
Returns
  • Creates an array of values by running each array element in array thru iteratee.
Examples
InputOutputStatus
local args = {true, false}
return utilsTable.map(args, tostring)
{"true", "false"}
Green check.svg
property shorthand
map(
  {
    {
      name = "Link",
      triforce = "Courage",
    },
    {
      name = "Zelda",
      triforce = "Wisdom",
    },
    {
      name = "Ganon",
      triforce = "Power",
    },
  },
  "triforce"
)
{"Courage", "Wisdom", "Power"}
Green check.svg
_map(iteratee)(array)
Returns
  • Creates an array of values by running each array element in array thru iteratee.
Examples
InputOutputStatus
property shorthand
_map("triforce")({
  {
    name = "Link",
    triforce = "Courage",
  },
  {
    name = "Zelda",
    triforce = "Wisdom",
  },
  {
    name = "Ganon",
    triforce = "Power",
  },
})
{"Courage", "Wisdom", "Power"}
Green check.svg

min

min(array)

Returns
  • The maximum value of array, or nil if empty.
Examples
InputOutputStatus
min({3, 2, 1})
1
Green check.svg
min({})
nil
Green check.svg

max

max(array)

Returns
  • The maximum value of array, or nil if empty.
Examples
InputOutputStatus
max({5, 10, 20})
20
Green check.svg
max({})
nil
Green check.svg

padNils

padNils_padNils
padNils(array, [padValue], [max])
Parameters
Returns
  • Returns a version of array such that nil-valued indices up to and including max are replaced with padValue.
Examples
InputOutputStatus
padNils({[2] = 2, [4] = 4 }, 0)
{0, 2, 0, 4}
Green check.svg
padNils({[2] = 2, [4] = 4 }, 0, 1)
{0, 2, [4] = 4}
Green check.svg
padNils({[2] = 2, [4] = 4 }, 0, 6)
{0, 2, 0, 4, 0, 0}
Green check.svg
padNils({"foo", [3] = "baz"})
{"foo", "", "baz"}
Green check.svg
_padNils([padValue], [max])(array)
Parameters
Returns
  • Returns a version of array such that nil-valued indices up to and including max are replaced with padValue.
Examples
InputOutputStatus
_padNils(0)({
  [2] = 2,
  [4] = 4,
})
{0, 2, 0, 4}
Green check.svg
_padNils(0, 1)({
  [2] = 2,
  [4] = 4,
})
{0, 2, [4] = 4}
Green check.svg
_padNils(0, 6)({
  [2] = 2,
  [4] = 4,
})
{0, 2, 0, 4, 0, 0}
Green check.svg
_padNils(nil)({"foo", [3] = "baz"})
{"foo", "", "baz"}
Green check.svg

partition

partition_partition
partition(array, iteratee)
Returns
  • array elements that iteratee returns truthy for.
  • array elements that iteratee returns falsey for.
Examples
InputOutputStatus
local tbl = {"foo", "bar", "baz", "quux"}
return utilsTable.partition(tbl, function(str)
  return str:sub(1, 1) ~= "b"
end)
{"foo", "quux"}
Green check.svg
{"bar", "baz"}
Green check.svg
property shorthand
partition(
  {
    {
      canon = true,
      game = "The Wind Waker",
    },
    {
      canon = true,
      game = "Twilight Princess",
    },
    {
      canon = false,
      game = "Tingle's Rosy Rupeeland",
    },
  },
  "canon"
)
{
  {
    canon = true,
    game = "The Wind Waker",
  },
  {
    canon = true,
    game = "Twilight Princess",
  },
}
Green check.svg
{
  {
    canon = false,
    game = "Tingle's Rosy Rupeeland",
  },
}
Green check.svg
isMatch shorthand
partition(
  {
    {
      type = "main",
      game = "The Wind Waker",
    },
    {
      type = "main",
      game = "Twilight Princess",
    },
    {
      type = "spinoff",
      game = "Tingle's Rosy Rupeeland",
    },
  },
  { type = "main" }
)
{
  {
    type = "main",
    game = "The Wind Waker",
  },
  {
    type = "main",
    game = "Twilight Princess",
  },
}
Green check.svg
{
  {
    type = "spinoff",
    game = "Tingle's Rosy Rupeeland",
  },
}
Green check.svg
_partition(iteratee)(array)
Returns
  • array elements that iteratee returns truthy for.
  • array elements that iteratee returns falsey for.
Examples
InputOutputStatus
property shorthand
_partition("canon")({
  {
    canon = true,
    game = "The Wind Waker",
  },
  {
    canon = true,
    game = "Twilight Princess",
  },
  {
    canon = false,
    game = "Tingle's Rosy Rupeeland",
  },
})
{
  {
    canon = true,
    game = "The Wind Waker",
  },
  {
    canon = true,
    game = "Twilight Princess",
  },
}
Green check.svg
{
  {
    canon = false,
    game = "Tingle's Rosy Rupeeland",
  },
}
Green check.svg
isMatch shorthand
_partition({ type = "main" })({
  {
    game = "The Wind Waker",
    type = "main",
  },
  {
    game = "Twilight Princess",
    type = "main",
  },
  {
    game = "Tingle's Rosy Rupeeland",
    type = "spinoff",
  },
})
{
  {
    game = "The Wind Waker",
    type = "main",
  },
  {
    game = "Twilight Princess",
    type = "main",
  },
}
Green check.svg
{
  {
    game = "Tingle's Rosy Rupeeland",
    type = "spinoff",
  },
}
Green check.svg

reverse

reverse(array)

Returns
  • Reverses array so that the first element becomes the last, the second element becomes the second to last, and so on.
Examples
InputOutputStatus
reverse({1, 2, 3})
{3, 2, 1}
Green check.svg
Ignores non-consecutive integer keys
reverse({1, 2, 3, [100] = 100})
{3, 2, 1}
Green check.svg

slice

slice_slice
slice(array, start, [end])
Parameters
Returns
  • Creates a slice of array from start up to and including end.
Examples
InputOutputStatus
slice({"a", "b", "c"}, 1, 2)
{"a", "b"}
Green check.svg
slice({"a", "b", "c"}, 2)
{"b", "c"}
Green check.svg
_slice(start, [end])(array)
Parameters
Returns
  • Creates a slice of array from start up to and including end.
Examples
InputOutputStatus
_slice(1, 2)({"a", "b", "c"})
{"a", "b"}
Green check.svg
_slice(2)({"a", "b", "c"})
{"b", "c"}
Green check.svg

sortBy

sortBy_sortBy
sortBy(array, iteratees)
Returns
  • An array of elements, sorted in ascending order by the results of running each element in array thru each iteratee. This method performs an unstable sort, that is, it does not guarantee that the original order of equal elements will be preserved.
Examples
InputOutputStatus
local games = {
  {
    game = "LA",
    systems = {"GB", "GBC", "3DS VC"}
  },
  {
    game = "OoT",
    systems = {"N64", "GCN", "iQue", "Wii VC", "Wii U VC"},
  },
  {
    game = "TWW",
    systems = {"GCN", "Wii U"},
  },
}
return utilsTable.sortBy(games, function(game)
  return #game.systems
end)
{
  {
    game = "TWW",
    systems = {"GCN", "Wii U"},
  },
  {
    game = "LA",
    systems = {"GB", "GBC", "3DS VC"},
  },
  {
    game = "OoT",
    systems = {"N64", "GCN", "iQue", "Wii VC", "Wii U VC"},
  },
}
Green check.svg
property shorthand
sortBy(
  {
    {
      name = "Link",
      age = 10,
    },
    {
      name = "Zelda",
      age = 10,
    },
    {
      name = "Zelda",
      age = 17,
    },
    {
      name = "Link",
      age = 17,
    },
  },
  "name"
)
{
  {
    name = "Link",
    age = 10,
  },
  {
    name = "Link",
    age = 17,
  },
  {
    name = "Zelda",
    age = 17,
  },
  {
    name = "Zelda",
    age = 10,
  },
}
Green check.svg
Multiple sort criteria
sortBy(
  {
    {
      name = "Link",
      age = 10,
    },
    {
      name = "Zelda",
      age = 10,
    },
    {
      name = "Zelda",
      age = 17,
    },
    {
      name = "Link",
      age = 17,
    },
  },
  {"name", "age"}
)
{
  {
    name = "Link",
    age = 10,
  },
  {
    name = "Link",
    age = 17,
  },
  {
    name = "Zelda",
    age = 10,
  },
  {
    name = "Zelda",
    age = 17,
  },
}
Green check.svg
_sortBy(iteratees)(array)
Returns
  • An array of elements, sorted in ascending order by the results of running each element in array thru each iteratee. This method performs an unstable sort, that is, it does not guarantee that the original order of equal elements will be preserved.
Examples
InputOutputStatus
property shorthand
_sortBy("name")({
  {
    name = "Link",
    age = 10,
  },
  {
    name = "Zelda",
    age = 10,
  },
  {
    name = "Zelda",
    age = 17,
  },
  {
    name = "Link",
    age = 17,
  },
})
{
  {
    name = "Link",
    age = 10,
  },
  {
    name = "Link",
    age = 17,
  },
  {
    name = "Zelda",
    age = 17,
  },
  {
    name = "Zelda",
    age = 10,
  },
}
Green check.svg
Multiple sort criteria
_sortBy({"name", "age"})({
  {
    name = "Link",
    age = 10,
  },
  {
    name = "Zelda",
    age = 10,
  },
  {
    name = "Zelda",
    age = 17,
  },
  {
    name = "Link",
    age = 17,
  },
})
{
  {
    name = "Link",
    age = 10,
  },
  {
    name = "Link",
    age = 17,
  },
  {
    name = "Zelda",
    age = 10,
  },
  {
    name = "Zelda",
    age = 17,
  },
}
Green check.svg

tail

tail(array)

Returns
  • Gets all but the first element of array.
Examples
InputOutputStatus
tail({1, 2, 3})
{2, 3}
Green check.svg
tail({})
{}
Green check.svg

take

take_take
take(array, n)
Returns
  • Creates a slice of array with n elements taken from the beginning.
Examples
InputOutputStatus
take({1, 2, 3}, 2)
{1, 2}
Green check.svg
take({1, 2, 3}, 5)
{1, 2, 3}
Green check.svg
take({1, 2, 3}, 0)
{}
Green check.svg
_take(n)(array)
Returns
  • Creates a slice of array with n elements taken from the beginning.
Examples
InputOutputStatus
_take(2)({1, 2, 3})
{1, 2}
Green check.svg
_take(5)({1, 2, 3})
{1, 2, 3}
Green check.svg
_take(0)({1, 2, 3})
{}
Green check.svg

takeWhile

takeWhile_takeWhile
takeWhile(array, predicate)
Returns
  • Creates a slice of array with elements taken from the beginning. Elements are taken until predicate returns falsey.
Examples
InputOutputStatus
local games = {"TLoZ", "TAoL", "ALttP", "LA", "OoT", "MM"}
local gamesBeforeOoT = utilsTable.takeWhile(games, function(game) 
  return game ~= "OoT"
end)
return gamesBeforeOoT
{"TLoZ", "TAoL", "ALttP", "LA"}
Green check.svg
property shorthand
takeWhile(
  {
    {
      is2D = true,
      game = "TLoZ",
    },
    {
      is2D = true,
      game = "TAoL",
    },
    {
      is2D = true,
      game = "ALttP",
    },
    {
      is2D = true,
      game = "LA",
    },
    {
      is2D = false,
      game = "OoT",
    },
    {
      is2D = true,
      game = "TMC",
    },
  },
  "is2D"
)
{
  {
    is2D = true,
    game = "TLoZ",
  },
  {
    is2D = true,
    game = "TAoL",
  },
  {
    is2D = true,
    game = "ALttP",
  },
  {
    is2D = true,
    game = "LA",
  },
}
Green check.svg
isMatch shorthand
takeWhile(
  {
    {
      system = "console",
      game = "TLoZ",
    },
    {
      system = "console",
      game = "TAoL",
    },
    {
      system = "console",
      game = "ALttP",
    },
    {
      system = "handheld",
      game = "LA",
    },
    {
      system = "console",
      game = "OoT",
    },
  },
  { system = "console" }
)
{
  {
    system = "console",
    game = "TLoZ",
  },
  {
    system = "console",
    game = "TAoL",
  },
  {
    system = "console",
    game = "ALttP",
  },
}
Green check.svg
_takeWhile(predicate)(array)
Returns
  • Creates a slice of array with elements taken from the beginning. Elements are taken until predicate returns falsey.
Examples
InputOutputStatus
property shorthand
_takeWhile("is2D")({
  {
    game = "TLoZ",
    is2D = true,
  },
  {
    game = "TAoL",
    is2D = true,
  },
  {
    game = "ALttP",
    is2D = true,
  },
  {
    game = "LA",
    is2D = true,
  },
  {
    game = "OoT",
    is2D = false,
  },
  {
    game = "TMC",
    is2D = true,
  },
})
{
  {
    game = "TLoZ",
    is2D = true,
  },
  {
    game = "TAoL",
    is2D = true,
  },
  {
    game = "ALttP",
    is2D = true,
  },
  {
    game = "LA",
    is2D = true,
  },
}
Green check.svg
isMatch shorthand
_takeWhile({ system = "console" })({
  {
    game = "TLoZ",
    system = "console",
  },
  {
    game = "TAoL",
    system = "console",
  },
  {
    game = "ALttP",
    system = "console",
  },
  {
    game = "LA",
    system = "handheld",
  },
  {
    game = "OoT",
    system = "console",
  },
})
{
  {
    game = "TLoZ",
    system = "console",
  },
  {
    game = "TAoL",
    system = "console",
  },
  {
    game = "ALttP",
    system = "console",
  },
}
Green check.svg

unique

unique(array)

Returns
  • A copy of array but without the duplicate values. Elements are deep-compared. he order of result values is determined by the order they occur in the array.
Examples
InputOutputStatus
unique({
  1,
  2,
  2,
  { foo = "bar" },
  { foo = "quux" },
  { foo = "bar" },
})
{
  1,
  2,
  { foo = "bar" },
  { foo = "quux" },
}
Green check.svg

uniqueBy

uniqueBy_uniqueBy
uniqueBy(array, iteratee)
Returns
  • A copy of array but without the duplicate values. iteratee is invoked for each element in array to generate the criterion by which uniqueness is computed.
Examples
InputOutputStatus
local games = {
  { title = "LA", game = "LA" },
  { title = "OoT", game = "OoT" },
  { title = "LADX", game = "LA" },
  { title = "OoT3D", game = "OoT" },
  { title = "LANS", game = "LA" },
}
return utilsTable.uniqueBy(games, function(game) return game.game end)
{
  {
    title = "LA",
    game = "LA",
  },
  {
    title = "OoT",
    game = "OoT",
  },
}
Green check.svg
property shorthand
uniqueBy(
  {
    {
      title = "LA",
      game = "LA",
    },
    {
      title = "OoT",
      game = "OoT",
    },
    {
      title = "LADX",
      game = "LA",
    },
    {
      title = "OoT3D",
      game = "OoT",
    },
    {
      title = "LANS",
      game = "LA",
    },
  },
  "game"
)
{
  {
    title = "LA",
    game = "LA",
  },
  {
    title = "OoT",
    game = "OoT",
  },
}
Green check.svg
_uniqueBy(iteratee)(array)
Returns
  • A copy of array but without the duplicate values. iteratee is invoked for each element in array to generate the criterion by which uniqueness is computed.
Examples
InputOutputStatus
property shorthand
_uniqueBy("game")({
  {
    title = "LA",
    game = "LA",
  },
  {
    title = "OoT",
    game = "OoT",
  },
  {
    title = "LADX",
    game = "LA",
  },
  {
    title = "OoT3D",
    game = "OoT",
  },
  {
    title = "LANS",
    game = "LA",
  },
})
{
  {
    title = "LA",
    game = "LA",
  },
  {
    title = "OoT",
    game = "OoT",
  },
}
Green check.svg

zip

zip(arrays, [padValue])

Parameters
Returns
  • Creates a transposition of arrays. 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 arrays not be of the same length, the resulting groups can be padded to the same length with padValue.
Examples
InputOutputStatus
zip({
  {1, 2},
  {"a", "b"},
  {true, false},
})
{
  {1, "a", true},
  {2, "b", false},
}
Green check.svg
zip(
  {
    {1, 2},
    {"a", "b", "c"},
    {true},
  },
  ""
)
{
  {1, "a", true},
  {2, "b", ""},
  {"", "c", ""},
}
Green check.svg
zip({
  {1, 2},
  {"a", "b", "c"},
  {true},
})
{
  {1, "a", true},
  {2, "b"},
  { [2] = "c" },
}
Green check.svg

local p = {}
local h = {}

local inspect = require("Module:UtilsTable/Inspect")
local utilsNumber = require("Module:UtilsNumber")

-- 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 MAX_SINGLELINE = 50
function p.print(tbl, singleLine)
	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 = {}
	for i, v in ipairs(array) do
		if not p.keyOf(other, v) then
			table.insert(result, v)
		end
	end
	return result
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.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
	for _, v in ipairs(array) do
		min = math.min(min or utilsNumber.MAX, v)
	end
	return min
end

function p.max(array)
	local max
	for _, v in ipairs(array) do
		max = math.max(max or utilsNumber.MIN, 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.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 = {
	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" },
	},
	zip = {
		arrays = {
			type = "array",
			required = true,
			items = { type = "any" },
		},
		padValue = {
			type = "any",
		},
	},
}

local propertyShorthand = "<code>{{Sect|property}}</code> shorthand"
local isMatchShorthand = "<code>{{Sect|isMatch}}</code> shorthand"

p.Documentation = {
	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" }},
						}
					},
				},
				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},
						},
					},
				},
				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" },
							},
						},
					},
				},
				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},
							}
						}
					},
				},
			},
		},
	},
}

return p