Mô đun:TableTools
| Mô đun Lua này được sử dụng trong thông báo hệ thống, và ở khoảng 726.000 trang, chiếm ≈ 16% tổng số trang. Thay đổi đến nó có thể dẫn đến thay đổi ngay lập tức giao diện người dùng Wikipedia. Để tránh gây lỗi trên quy mô lớn và tải máy chủ không cần thiết, tất cả thay đổi cần được thử nghiệm ở trang con /sandbox, /testcases của mô đun, hoặc ở chỗ thử mô đun. Các thay đổi đã được thử nghiệm có thể thêm vào mô đun bằng một sửa đổi duy nhất. Xin hãy thảo luận các thay đổi tại trang thảo luận trước khi áp dụng sửa đổi. |
| Mô đun này được xếp loại là đã sẵn sàng để sử dụng rộng rãi. Nó đã đạt đến mức độ hoàn thiện, được coi là khá ổn định và không có lỗi, và có thể được sử dụng bất kỳ chỗ nào nếu phù hợp. Nó có thể được nêu trên các trang trợ giúp cũng như các tài liệu Wikipedia khác làm tùy chọn tìm hiểu cho người dùng mới. Để giảm tải tài nguyên máy chủ và tránh tạo đầu ra gây hại, mọi cải tiến nên được thực hiện thông qua việc kiểm thử tại chỗ thử thay vì sửa đổi "thử và sai" lặp đi lặp lại liên tục. |
| Trang mô đun này đang bị khóa không cho sửa đổi. Xem quy định khóa trang và nhật trình khóa để biết thêm chi tiết. Vui lòng thảo luận bất kỳ thay đổi nào tại trang thảo luận; bạn có thể yêu cầu sửa trang này để yêu cầu bảo quản viên sửa đổi trang nếu sửa đổi đó là sửa đổi không gây hại hoặc được chấp nhận sự đồng thuận. Bạn cũng có thể yêu cầu mở khóa trang này. |
| Mô đun này phụ thuộc vào các mô đun sau: |
Mô đun này bao gồm một số hàm để xử lý các bảng (table) Lua. Đây là một siêu mô đun (meta-module), được thiết kế để các mô đun Lua khác gọi và không nên được gọi trực tiếp từ #invoke.
Tải mô đun
[sửa mã nguồn]Để sử dụng bất kỳ hàm nào, trước tiên bạn phải tải mô đun.
local TableTools = require('Module:TableTools')
isPositiveInteger
[sửa mã nguồn]TableTools.isPositiveInteger(value)
Trả về true nếu value là một số nguyên dương, và false nếu không phải. Mặc dù hàm này không thao tác trực tiếp trên các bảng, nó được đưa vào đây vì rất hữu ích để xác định xem một khóa bảng nhất định nằm trong phần mảng hay phần băm (hash) của bảng.
isNan
[sửa mã nguồn]TableTools.isNan(value)
Trả về true nếu value là một giá trị NaN (Not a Number), và false nếu không phải. Mặc dù hàm này không thao tác trên các bảng, nó được đưa vào đây vì rất hữu ích để xác định xem một giá trị có thể là một khóa bảng hợp lệ hay không. (Lua sẽ báo lỗi nếu giá trị NaN được sử dụng làm khóa bảng.)
shallowClone
[sửa mã nguồn]TableTools.shallowClone(t)
Trả về bản sao nông (shallow clone) của một bảng. Giá trị trả về là một bảng mới, nhưng tất cả các bảng con và hàm đều được chia sẻ (tham chiếu). Các metamethod được tôn trọng, nhưng bảng trả về sẽ không có metatable của riêng nó. Nếu bạn muốn tạo một bảng mới không chia sẻ các bảng con và có chuyển giao metatable, bạn có thể sử dụng mw.clone để thay thế. Nếu bạn muốn tạo một bảng mới không chia sẻ các bảng con và không chuyển giao metatable, hãy sử dụng deepCopy với tùy chọn noMetatable.
removeDuplicates
[sửa mã nguồn]TableTools.removeDuplicates(t)
Loại bỏ các giá trị trùng lặp khỏi một mảng. Hàm này chỉ được thiết kế để hoạt động với các mảng tiêu chuẩn: các khóa không phải là số nguyên dương sẽ bị bỏ qua, cũng như tất cả các giá trị sau giá trị nil đầu tiên. (Đối với các mảng có chứa giá trị nil, bạn có thể sử dụng compressSparseArray trước). Hàm cố gắng bảo toàn thứ tự của mảng: giá trị không trùng lặp xuất hiện sớm nhất được giữ lại, và tất cả các giá trị trùng lặp sau đó sẽ bị xóa. Ví dụ, đối với bảng {5, 4, 4, 3, 4, 2, 2, 1}, removeDuplicates sẽ trả về {5, 4, 3, 2, 1}.
numKeys
[sửa mã nguồn]TableTools.numKeys(t)
Nhận vào một bảng t và trả về một mảng chứa các số của bất kỳ khóa số nguyên dương nào có giá trị khác nil, được sắp xếp theo thứ tự số học. Ví dụ, đối với bảng {'foo', nil, 'bar', 'baz', a = 'b'}, numKeys sẽ trả về {1, 3, 4}.
affixNums
[sửa mã nguồn]TableTools.affixNums(t, prefix, suffix)
Nhận vào một bảng t và trả về một mảng chứa các số của các khóa có tiền tố tùy chọn prefix và hậu tố tùy chọn suffix. Ví dụ, đối với bảng {a1 = 'foo', a3 = 'bar', a6 = 'baz'} và tiền tố 'a', affixNums sẽ trả về {1, 3, 6}. Tất cả các ký tự trong prefix và suffix đều được diễn giải theo nghĩa đen (không phải regex).
Xem {{#invoke:params|call_for_each_group}} và {{#invoke:params|grouping_by_calling}} để thực hiện điều tương tự từ một bản mẫu wiki.
numData
[sửa mã nguồn]TableTools.numData(t, compress)
Với một bảng có các khóa như "foo1", "bar1", "foo2", và "baz2", hàm trả về một bảng gồm các bảng con theo định dạng { [1] = {foo = 'text', bar = 'text'}, [2] = {foo = 'text', baz = 'text'} }. Các khóa không kết thúc bằng một số nguyên được lưu trong một bảng con tên là "other". Tùy chọn compress sẽ nén bảng để có thể lặp qua bằng ipairs.
compressSparseArray
[sửa mã nguồn]TableTools.compressSparseArray(t)
Nhận vào một mảng t có một hoặc nhiều giá trị nil, và loại bỏ các giá trị nil trong khi vẫn giữ nguyên thứ tự, để mảng có thể được duyệt an toàn bằng ipairs. Bất kỳ khóa nào không phải là số nguyên dương đều bị loại bỏ. Ví dụ, đối với bảng {1, nil, foo = 'bar', 3, 2}, compressSparseArray sẽ trả về {1, 3, 2}.
Xem {{#invoke:params|squeezing}} để thực hiện điều tương tự từ một bản mẫu wiki.
sparseIpairs
[sửa mã nguồn]TableTools.sparseIpairs(t)
Đây là một hàm lặp (iterator function) để duyệt một mảng thưa (sparse array) t. Nó tương tự như ipairs, nhưng sẽ tiếp tục lặp cho đến khóa số cao nhất, trong khi ipairs có thể dừng lại sau giá trị nil đầu tiên. Bất kỳ khóa nào không phải là số nguyên dương đều bị bỏ qua.
Thông thường sparseIpairs được sử dụng trong vòng lặp for chung.
for i, v in TableTools.sparseIpairs(t) do
-- khối mã lệnh
end
Lưu ý rằng sparseIpairs sử dụng hàm pairs trong quá trình thực thi. Mặc dù một số khóa bảng có vẻ bị bỏ qua, nhưng thực tế tất cả các khóa bảng đều được truy cập khi nó chạy.
size
[sửa mã nguồn]TableTools.size(t)
Tìm kích thước của một bảng cặp khóa/giá trị (mảng liên kết). Ví dụ, đối với {foo = 'foo', bar = 'bar'}, size sẽ trả về 2. Hàm cũng hoạt động trên các mảng, nhưng đối với mảng, việc sử dụng toán tử # sẽ hiệu quả hơn. Lưu ý rằng để tìm kích thước, hàm này sử dụng hàm pairs để lặp qua tất cả các khóa.
keysToList
[sửa mã nguồn]TableTools.keysToList(t, keySort, checked)
Trả về danh sách các khóa trong một bảng, được sắp xếp bằng hàm so sánh mặc định hoặc hàm keySort tùy chỉnh. Hàm này tuân theo các quy tắc giống như hàm comp được cung cấp cho table.sort. Nếu keySort là false, việc sắp xếp sẽ không được thực hiện. Đặt checked thành true để bỏ qua kiểm tra kiểu nội bộ.
sortedPairs
[sửa mã nguồn]TableTools.sortedPairs(t, keySort)
Lặp qua một bảng, với các khóa được sắp xếp bằng hàm keysToList. Nếu chỉ có các khóa số, dùng sparseIpairs có thể sẽ hiệu quả hơn.
isArray
[sửa mã nguồn]TableTools.isArray(value)
Trả về true nếu value là một bảng và tất cả các khóa là các số nguyên liên tiếp bắt đầu từ 1.
isArrayLike
[sửa mã nguồn]TableTools.isArrayLike(value)
Trả về true nếu value có thể lặp (iterable) và tất cả các khóa là các số nguyên liên tiếp bắt đầu từ 1.
invert
[sửa mã nguồn]TableTools.invert(arr)
Hoán đổi các khóa và giá trị trong một mảng. Ví dụ, invert{ "a", "b", "c" } cho kết quả { a=1, b=2, c=3 }.
listToSet
[sửa mã nguồn]TableTools.listToSet(arr)
Tạo một tập hợp (set) từ phần mảng của bảng arr. Việc lập chỉ mục tập hợp bằng bất kỳ giá trị nào của mảng sẽ trả về true. Ví dụ, listToSet{ "a", "b", "c" } cho kết quả { a=true, b=true, c=true }.
deepCopy
[sửa mã nguồn]TableTools.deepCopy(orig, noMetatable, alreadySeen)
Tạo một bản sao của bảng orig. Giống như mw.clone, tất cả các giá trị không phải là hàm đều được sao chép và danh tính của các bảng được bảo toàn. Nếu noMetatable là true, thì metatable (nếu có) sẽ không được sao chép. Có thể sao chép các bảng được tải bằng mw.loadData.
Tương tự như mw.clone, nhưng mw.clone không thể sao chép các bảng được tải bằng mw.loadData và không cho phép tùy chọn không sao chép metatable.
sparseConcat
[sửa mã nguồn]TableTools.sparseConcat(t, sep, i, j)
Nối tất cả các giá trị trong bảng được đánh chỉ mục bằng một số nguyên dương, theo thứ tự. Ví dụ, sparseConcat{ "a", nil, "c", "d" } cho kết quả "acd" và sparseConcat{ nil, "b", "c", "d" } cho kết quả "bcd".
length
[sửa mã nguồn]TableTools.length(t, prefix)
Tìm độ dài của một mảng hoặc một bảng dạng mảng (quasi-array) với các khóa có tiền tố tùy chọn prefix như "data1", "data2", v.v... Nó sử dụng thuật toán tìm kiếm mũ để tìm độ dài, nhằm sử dụng ít thao tác tra cứu bảng nhất có thể.
Thuật toán này hữu ích cho các mảng sử dụng metatable (ví dụ: frame.args) và cho các bảng dạng mảng. Đối với các mảng thông thường, chỉ cần sử dụng toán tử #, vì nó được triển khai bằng C và sẽ nhanh hơn.
inArray
[sửa mã nguồn]TableTools.inArray(array, searchElement)
TableTools.inArray(array, searchElement, fromIndex)
Trả về true nếu searchElement là một thành viên của mảng array, và false nếu ngược lại. Tương đương với hàm Array.prototype.includes() của JavaScript, ngoại trừ việc fromIndex bắt đầu từ 1 thay vì bắt đầu từ 0.
fromIndex
[sửa mã nguồn]fromIndex là chỉ mục bắt đầu tìm kiếm tùy chọn (bắt đầu từ 1). Nếu fromIndex không hiện diện, tất cả các giá trị trong mảng sẽ được tìm kiếm và mảng sẽ được xử lý như một bảng/mảng liên kết (nó sẽ được lặp qua bằng pairs()).
Nếu fromIndex hiện diện và là một số nguyên, mảng được coi là một mảng/chuỗi/danh sách thông thường (được đánh chỉ mục bằng các khóa số nguyên liên tiếp bắt đầu từ 1, và được lặp qua bằng ipairs()). Chỉ các giá trị có chỉ mục từ fromIndex trở lên mới được tìm kiếm.
Trong các ví dụ sau, #array đại diện cho độ dài của phần khóa số nguyên của mảng.
- Nếu
fromIndex < 0, nó sẽ đếm ngược từ cuối mảng, ví dụ: giá trị-1sẽ chỉ tìm kiếm phần tử có khóa số nguyên cuối cùng trong mảng. NếufromIndex <= (-1 * #array), toàn bộ phần khóa số nguyên của mảng sẽ được tìm kiếm. - Nếu
fromIndex = 0, nó sẽ được coi là1và toàn bộ phần khóa số nguyên của mảng sẽ được tìm kiếm. - Nếu
fromIndex > #array, mảng sẽ không được tìm kiếm và trả vềfalse.
merge
[sửa mã nguồn]TableTools.merge(...)
Với đầu vào là các mảng, trả về một mảng chứa các phần tử của từng mảng đầu vào theo thứ tự nối tiếp.
extend
[sửa mã nguồn]TableTools.extend(arr1, arr2)
Mở rộng mảng đầu tiên tại chỗ (in-place) bằng cách nối thêm tất cả các phần tử từ mảng thứ hai vào.
Xem thêm
[sửa mã nguồn]- {{#gọi:params}}
--[[
------------------------------------------------------------------------------------
-- TableTools --
-- --
-- This module includes a number of functions for dealing with Lua tables. --
-- It is a meta-module, meant to be called from other Lua modules, and should --
-- not be called directly from #invoke. --
------------------------------------------------------------------------------------
--]]
local libraryUtil = require('libraryUtil')
local p = {}
-- Define often-used variables and functions.
local floor = math.floor
local infinity = math.huge
local checkType = libraryUtil.checkType
local checkTypeMulti = libraryUtil.checkTypeMulti
--[[
------------------------------------------------------------------------------------
-- isPositiveInteger
--
-- This function returns true if the given value is a positive integer, and false
-- if not. Although it doesn't operate on tables, it is included here as it is
-- useful for determining whether a given table key is in the array part or the
-- hash part of a table.
------------------------------------------------------------------------------------
--]]
function p.isPositiveInteger(v)
return type(v) == 'number' and v >= 1 and floor(v) == v and v < infinity
end
--[[
------------------------------------------------------------------------------------
-- isNan
--
-- This function returns true if the given number is a NaN value, and false
-- if not. Although it doesn't operate on tables, it is included here as it is
-- useful for determining whether a value can be a valid table key. Lua will
-- generate an error if a NaN is used as a table key.
------------------------------------------------------------------------------------
--]]
function p.isNan(v)
return type(v) == 'number' and tostring(v) == '-nan'
end
--[[
------------------------------------------------------------------------------------
-- shallowClone
--
-- This returns a clone of a table. The value returned is a new table, but all
-- subtables and functions are shared. Metamethods are respected, but the returned
-- table will have no metatable of its own.
------------------------------------------------------------------------------------
--]]
function p.shallowClone(t)
local ret = {}
for k, v in pairs(t) do
ret[k] = v
end
return ret
end
--[[
------------------------------------------------------------------------------------
-- removeDuplicates
--
-- This removes duplicate values from an array. Non-positive-integer keys are
-- ignored. The earliest value is kept, and all subsequent duplicate values are
-- removed, but otherwise the array order is unchanged.
------------------------------------------------------------------------------------
--]]
function p.removeDuplicates(t)
checkType('removeDuplicates', 1, t, 'table')
local isNan = p.isNan
local ret, exists = {}, {}
for i, v in ipairs(t) do
if isNan(v) then
-- NaNs can't be table keys, and they are also unique, so we don't need to check existence.
ret[#ret + 1] = v
else
if not exists[v] then
ret[#ret + 1] = v
exists[v] = true
end
end
end
return ret
end
--[[
------------------------------------------------------------------------------------
-- numKeys
--
-- This takes a table and returns an array containing the numbers of any numerical
-- keys that have non-nil values, sorted in numerical order.
------------------------------------------------------------------------------------
--]]
function p.numKeys(t)
checkType('numKeys', 1, t, 'table')
local isPositiveInteger = p.isPositiveInteger
local nums = {}
for k, v in pairs(t) do
if isPositiveInteger(k) then
nums[#nums + 1] = k
end
end
table.sort(nums)
return nums
end
--[[
------------------------------------------------------------------------------------
-- affixNums
--
-- This takes a table and returns an array containing the numbers of keys with the
-- specified prefix and suffix. For example, for the table
-- {a1 = 'foo', a3 = 'bar', a6 = 'baz'} and the prefix "a", affixNums will
-- return {1, 3, 6}.
------------------------------------------------------------------------------------
--]]
function p.affixNums(t, prefix, suffix)
checkType('affixNums', 1, t, 'table')
checkType('affixNums', 2, prefix, 'string', true)
checkType('affixNums', 3, suffix, 'string', true)
local function cleanPattern(s)
-- Cleans a pattern so that the magic characters ()%.[]*+-?^$ are interpreted literally.
s = s:gsub('([%(%)%%%.%[%]%*%+%-%?%^%$])', '%%%1')
return s
end
prefix = prefix or ''
suffix = suffix or ''
prefix = cleanPattern(prefix)
suffix = cleanPattern(suffix)
local pattern = '^' .. prefix .. '([1-9]%d*)' .. suffix .. '$'
local nums = {}
for k, v in pairs(t) do
if type(k) == 'string' then
local num = mw.ustring.match(k, pattern)
if num then
nums[#nums + 1] = tonumber(num)
end
end
end
table.sort(nums)
return nums
end
--[[
------------------------------------------------------------------------------------
-- numData
--
-- Given a table with keys like ("foo1", "bar1", "foo2", "baz2"), returns a table
-- of subtables in the format
-- { [1] = {foo = 'text', bar = 'text'}, [2] = {foo = 'text', baz = 'text'} }
-- Keys that don't end with an integer are stored in a subtable named "other".
-- The compress option compresses the table so that it can be iterated over with
-- ipairs.
------------------------------------------------------------------------------------
--]]
function p.numData(t, compress)
checkType('numData', 1, t, 'table')
checkType('numData', 2, compress, 'boolean', true)
local ret = {}
for k, v in pairs(t) do
local prefix, num = mw.ustring.match(tostring(k), '^([^0-9]*)([1-9][0-9]*)$')
if num then
num = tonumber(num)
local subtable = ret[num] or {}
if prefix == '' then
-- Positional parameters match the blank string; put them at the start of the subtable instead.
prefix = 1
end
subtable[prefix] = v
ret[num] = subtable
else
local subtable = ret.other or {}
subtable[k] = v
ret.other = subtable
end
end
if compress then
local other = ret.other
ret = p.compressSparseArray(ret)
ret.other = other
end
return ret
end
--[[
------------------------------------------------------------------------------------
-- compressSparseArray
--
-- This takes an array with one or more nil values, and removes the nil values
-- while preserving the order, so that the array can be safely traversed with
-- ipairs.
------------------------------------------------------------------------------------
--]]
function p.compressSparseArray(t)
checkType('compressSparseArray', 1, t, 'table')
local ret = {}
local nums = p.numKeys(t)
for _, num in ipairs(nums) do
ret[#ret + 1] = t[num]
end
return ret
end
--[[
------------------------------------------------------------------------------------
-- sparseIpairs
--
-- This is an iterator for sparse arrays. It can be used like ipairs, but can
-- handle nil values.
------------------------------------------------------------------------------------
--]]
function p.sparseIpairs(t)
checkType('sparseIpairs', 1, t, 'table')
local nums = p.numKeys(t)
local i = 0
local lim = #nums
return function ()
i = i + 1
if i <= lim then
local key = nums[i]
return key, t[key]
else
return nil, nil
end
end
end
--[[
------------------------------------------------------------------------------------
-- size
--
-- This returns the size of a key/value pair table. It will also work on arrays,
-- but for arrays it is more efficient to use the # operator.
------------------------------------------------------------------------------------
--]]
function p.size(t)
checkType('size', 1, t, 'table')
local i = 0
for k in pairs(t) do
i = i + 1
end
return i
end
local function defaultKeySort(item1, item2)
-- "number" < "string", so numbers will be sorted before strings.
local type1, type2 = type(item1), type(item2)
if type1 ~= type2 then
return type1 < type2
else -- This will fail with table, boolean, function.
return item1 < item2
end
end
--[[
Returns a list of the keys in a table, sorted using either a default
comparison function or a custom keySort function.
]]
function p.keysToList(t, keySort, checked)
if not checked then
checkType('keysToList', 1, t, 'table')
checkTypeMulti('keysToList', 2, keySort, { 'function', 'boolean', 'nil' })
end
local list = {}
local index = 1
for key, value in pairs(t) do
list[index] = key
index = index + 1
end
if keySort ~= false then
keySort = type(keySort) == 'function' and keySort or defaultKeySort
table.sort(list, keySort)
end
return list
end
--[[
Iterates through a table, with the keys sorted using the keysToList function.
If there are only numerical keys, sparseIpairs is probably more efficient.
]]
function p.sortedPairs(t, keySort)
checkType('sortedPairs', 1, t, 'table')
checkType('sortedPairs', 2, keySort, 'function', true)
local list = p.keysToList(t, keySort, true)
local i = 0
return function()
i = i + 1
local key = list[i]
if key ~= nil then
return key, t[key]
else
return nil, nil
end
end
end
--[[
Returns true if all keys in the table are consecutive integers starting at 1.
--]]
function p.isArray(t)
checkType("isArray", 1, t, "table")
local i = 0
for k, v in pairs(t) do
i = i + 1
if t[i] == nil then
return false
end
end
return true
end
-- { "a", "b", "c" } -> { a = 1, b = 2, c = 3 }
function p.invert(array)
checkType("invert", 1, array, "table")
local map = {}
for i, v in ipairs(array) do
map[v] = i
end
return map
end
--[[
{ "a", "b", "c" } -> { ["a"] = true, ["b"] = true, ["c"] = true }
--]]
function p.listToSet(t)
checkType("listToSet", 1, t, "table")
local set = {}
for _, item in ipairs(t) do
set[item] = true
end
return set
end
--[[
Recursive deep copy function.
Preserves identities of subtables.
]]
local function _deepCopy(orig, includeMetatable, already_seen)
-- Stores copies of tables indexed by the original table.
already_seen = already_seen or {}
local copy = already_seen[orig]
if copy ~= nil then
return copy
end
if type(orig) == 'table' then
copy = {}
for orig_key, orig_value in pairs(orig) do
copy[deepcopy(orig_key, includeMetatable, already_seen)] = deepcopy(orig_value, includeMetatable, already_seen)
end
already_seen[orig] = copy
if includeMetatable then
local mt = getmetatable(orig)
if mt ~= nil then
local mt_copy = deepcopy(mt, includeMetatable, already_seen)
setmetatable(copy, mt_copy)
already_seen[mt] = mt_copy
end
end
else -- number, string, boolean, etc
copy = orig
end
return copy
end
function p.deepCopy(orig, noMetatable, already_seen)
checkType("deepCopy", 3, already_seen, "table", true)
return _deepCopy(orig, not noMetatable, already_seen)
end
--[[
Concatenates all values in the table that are indexed by a number, in order.
sparseConcat{ a, nil, c, d } => "acd"
sparseConcat{ nil, b, c, d } => "bcd"
]]
function p.sparseConcat(t, sep, i, j)
local list = {}
local list_i = 0
for _, v in p.sparseIpairs(t) do
list_i = list_i + 1
list[list_i] = v
end
return table.concat(list, sep, i, j)
end
--[[
-- Finds the length of an array, or of a quasi-array with keys such
-- as "data1", "data2", etc., using an exponental search algorithm.
-- It is similar to the operator #, but may return
-- a different value when there are gaps in the array portion of the table.
-- Intended to be used on data loaded with mw.loadData. For other tables, use #.
-- Note: #frame.args in frame object always be set to 0, regardless of
-- the number of unnamed template parameters, so use this function for
-- frame.args.
--]]
function p.length(t, prefix)
-- requiring module inline so that [[Module:Exponental search]]
-- which is only needed by this one function
-- doesn't get millions of transclusions
local expSearch = require("Mô đun:Exponential search")
checkType('length', 1, t, 'table')
checkType('length', 2, prefix, 'string', true)
return expSearch(function(i)
local key
if prefix then
key = prefix .. tostring(i)
else
key = i
end
return t[key] ~= nil
end) or 0
end
function p.inArray(arr, valueToFind)
checkType("inArray", 1, arr, "table")
-- if valueToFind is nil, error?
for _, v in ipairs(arr) do
if v == valueToFind then
return true
end
end
return false
end
return p