置顶公告:【置顶】关于临时开启评论区所有功能的公告(2022.10.22) | 【置顶】关于本站Widget恢复使用的公告
  • 你好~!欢迎来到萌娘百科镜像站!如需查看或编辑,请联系本站管理员注册账号。
  • 本镜像站和其他萌娘百科的镜像站无关,请注意分别。

Module:TableTools

猛汉♂百科,万男皆可猛的百科全书!转载请标注来源页面的网页链接,并声明引自猛汉百科。内容不可商用。
跳到导航 跳到搜索
Template-info.svg 模块文档  [创建] [刷新]
  1. --[[
  2. ------------------------------------------------------------------------------------
  3. -- TableTools --
  4. -- --
  5. -- This module includes a number of functions for dealing with Lua tables. --
  6. -- It is a meta-module, meant to be called from other Lua modules, and should --
  7. -- not be called directly from #invoke. --
  8. ------------------------------------------------------------------------------------
  9. --]]
  10. local libraryUtil = require('libraryUtil')
  11. local p = {}
  12. -- Define often-used variables and functions.
  13. local floor = math.floor
  14. local infinity = math.huge
  15. local checkType = libraryUtil.checkType
  16. --[[
  17. ------------------------------------------------------------------------------------
  18. -- isPositiveInteger
  19. --
  20. -- This function returns true if the given value is a positive integer, and false
  21. -- if not. Although it doesn't operate on tables, it is included here as it is
  22. -- useful for determining whether a given table key is in the array part or the
  23. -- hash part of a table.
  24. ------------------------------------------------------------------------------------
  25. --]]
  26. function p.isPositiveInteger(v)
  27. if type(v) == 'number' and v >= 1 and floor(v) == v and v < infinity then
  28. return true
  29. else
  30. return false
  31. end
  32. end
  33. --[[
  34. ------------------------------------------------------------------------------------
  35. -- isNan
  36. --
  37. -- This function returns true if the given number is a NaN value, and false
  38. -- if not. Although it doesn't operate on tables, it is included here as it is
  39. -- useful for determining whether a value can be a valid table key. Lua will
  40. -- generate an error if a NaN is used as a table key.
  41. ------------------------------------------------------------------------------------
  42. --]]
  43. function p.isNan(v)
  44. if type(v) == 'number' and tostring(v) == '-nan' then
  45. return true
  46. else
  47. return false
  48. end
  49. end
  50. --[[
  51. ------------------------------------------------------------------------------------
  52. -- shallowClone
  53. --
  54. -- This returns a clone of a table. The value returned is a new table, but all
  55. -- subtables and functions are shared. Metamethods are respected, but the returned
  56. -- table will have no metatable of its own.
  57. ------------------------------------------------------------------------------------
  58. --]]
  59. function p.shallowClone(t)
  60. local ret = {}
  61. for k, v in pairs(t) do
  62. ret[k] = v
  63. end
  64. return ret
  65. end
  66. --[[
  67. ------------------------------------------------------------------------------------
  68. -- removeDuplicates
  69. --
  70. -- This removes duplicate values from an array. Non-positive-integer keys are
  71. -- ignored. The earliest value is kept, and all subsequent duplicate values are
  72. -- removed, but otherwise the array order is unchanged.
  73. ------------------------------------------------------------------------------------
  74. --]]
  75. function p.removeDuplicates(t)
  76. checkType('removeDuplicates', 1, t, 'table')
  77. local isNan = p.isNan
  78. local ret, exists = {}, {}
  79. for i, v in ipairs(t) do
  80. if isNan(v) then
  81. -- NaNs can't be table keys, and they are also unique, so we don't need to check existence.
  82. ret[#ret + 1] = v
  83. else
  84. if not exists[v] then
  85. ret[#ret + 1] = v
  86. exists[v] = true
  87. end
  88. end
  89. end
  90. return ret
  91. end
  92. --[[
  93. ------------------------------------------------------------------------------------
  94. -- numKeys
  95. --
  96. -- This takes a table and returns an array containing the numbers of any numerical
  97. -- keys that have non-nil values, sorted in numerical order.
  98. ------------------------------------------------------------------------------------
  99. --]]
  100. function p.numKeys(t)
  101. checkType('numKeys', 1, t, 'table')
  102. local isPositiveInteger = p.isPositiveInteger
  103. local nums = {}
  104. for k, v in pairs(t) do
  105. if isPositiveInteger(k) then
  106. nums[#nums + 1] = k
  107. end
  108. end
  109. table.sort(nums)
  110. return nums
  111. end
  112. --[[
  113. ------------------------------------------------------------------------------------
  114. -- affixNums
  115. --
  116. -- This takes a table and returns an array containing the numbers of keys with the
  117. -- specified prefix and suffix. For example, for the table
  118. -- {a1 = 'foo', a3 = 'bar', a6 = 'baz'} and the prefix "a", affixNums will
  119. -- return {1, 3, 6}.
  120. ------------------------------------------------------------------------------------
  121. --]]
  122. function p.affixNums(t, prefix, suffix)
  123. checkType('affixNums', 1, t, 'table')
  124. checkType('affixNums', 2, prefix, 'string', true)
  125. checkType('affixNums', 3, suffix, 'string', true)
  126. local function cleanPattern(s)
  127. -- Cleans a pattern so that the magic characters ()%.[]*+-?^$ are interpreted literally.
  128. s = s:gsub('([%(%)%%%.%[%]%*%+%-%?%^%$])', '%%%1')
  129. return s
  130. end
  131. prefix = prefix or ''
  132. suffix = suffix or ''
  133. prefix = cleanPattern(prefix)
  134. suffix = cleanPattern(suffix)
  135. local pattern = '^' .. prefix .. '([1-9]%d*)' .. suffix .. '$'
  136. local nums = {}
  137. for k, v in pairs(t) do
  138. if type(k) == 'string' then
  139. local num = mw.ustring.match(k, pattern)
  140. if num then
  141. nums[#nums + 1] = tonumber(num)
  142. end
  143. end
  144. end
  145. table.sort(nums)
  146. return nums
  147. end
  148. --[[
  149. ------------------------------------------------------------------------------------
  150. -- numData
  151. --
  152. -- Given a table with keys like ("foo1", "bar1", "foo2", "baz2"), returns a table
  153. -- of subtables in the format
  154. -- { [1] = {foo = 'text', bar = 'text'}, [2] = {foo = 'text', baz = 'text'} }
  155. -- Keys that don't end with an integer are stored in a subtable named "other".
  156. -- The compress option compresses the table so that it can be iterated over with
  157. -- ipairs.
  158. ------------------------------------------------------------------------------------
  159. --]]
  160. function p.numData(t, compress)
  161. checkType('numData', 1, t, 'table')
  162. checkType('numData', 2, compress, 'boolean', true)
  163. local ret = {}
  164. for k, v in pairs(t) do
  165. local prefix, num = mw.ustring.match(tostring(k), '^([^0-9]*)([1-9][0-9]*)$')
  166. if num then
  167. num = tonumber(num)
  168. local subtable = ret[num] or {}
  169. if prefix == '' then
  170. -- Positional parameters match the blank string; put them at the start of the subtable instead.
  171. prefix = 1
  172. end
  173. subtable[prefix] = v
  174. ret[num] = subtable
  175. else
  176. local subtable = ret.other or {}
  177. subtable[k] = v
  178. ret.other = subtable
  179. end
  180. end
  181. if compress then
  182. local other = ret.other
  183. ret = p.compressSparseArray(ret)
  184. ret.other = other
  185. end
  186. return ret
  187. end
  188. --[[
  189. ------------------------------------------------------------------------------------
  190. -- compressSparseArray
  191. --
  192. -- This takes an array with one or more nil values, and removes the nil values
  193. -- while preserving the order, so that the array can be safely traversed with
  194. -- ipairs.
  195. ------------------------------------------------------------------------------------
  196. --]]
  197. function p.compressSparseArray(t)
  198. checkType('compressSparseArray', 1, t, 'table')
  199. local ret = {}
  200. local nums = p.numKeys(t)
  201. for _, num in ipairs(nums) do
  202. ret[#ret + 1] = t[num]
  203. end
  204. return ret
  205. end
  206. --[[
  207. ------------------------------------------------------------------------------------
  208. -- sparseIpairs
  209. --
  210. -- This is an iterator for sparse arrays. It can be used like ipairs, but can
  211. -- handle nil values.
  212. ------------------------------------------------------------------------------------
  213. --]]
  214. function p.sparseIpairs(t)
  215. checkType('sparseIpairs', 1, t, 'table')
  216. local nums = p.numKeys(t)
  217. local i = 0
  218. local lim = #nums
  219. return function ()
  220. i = i + 1
  221. if i <= lim then
  222. local key = nums[i]
  223. return key, t[key]
  224. else
  225. return nil, nil
  226. end
  227. end
  228. end
  229. --[[
  230. ------------------------------------------------------------------------------------
  231. -- size
  232. --
  233. -- This returns the size of a key/value pair table. It will also work on arrays,
  234. -- but for arrays it is more efficient to use the # operator.
  235. ------------------------------------------------------------------------------------
  236. --]]
  237. function p.size(t)
  238. checkType('size', 1, t, 'table')
  239. local i = 0
  240. for k in pairs(t) do
  241. i = i + 1
  242. end
  243. return i
  244. end
  245. return p