Jump to content
Wikibooks The Free Textbook Project

Module:TableTools

From Wikibooks, open books for an open world
Module documentation[create] [purge]
You might want to create a documentation page for this Scribunto module.
Editors can experiment in this module's sandbox (create | mirror) and testcases (create) pages.
Add categories to the /doc subpage. Subpages of this module.
 ------------------------------------------------------------------------------------
 -- 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. --
 ------------------------------------------------------------------------------------

 locallibraryUtil=require('libraryUtil')

 localp={}

 -- Define often-used variables and functions.
 localfloor=math.floor
 localinfinity=math.huge
 localcheckType=libraryUtil.checkType
 localcheckTypeMulti=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.
 ------------------------------------------------------------------------------------
 functionp.isPositiveInteger(v)
 returntype(v)=='number'andv>=1andfloor(v)==vandv<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.
 ------------------------------------------------------------------------------------
 functionp.isNan(v)
 returntype(v)=='number'andv~=v
 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.
 ------------------------------------------------------------------------------------
 functionp.shallowClone(t)
 checkType('shallowClone',1,t,'table')
 localret={}
 fork,vinpairs(t)do
 ret[k]=v
 end
 returnret
 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.
 ------------------------------------------------------------------------------------
 functionp.removeDuplicates(arr)
 checkType('removeDuplicates',1,arr,'table')
 localisNan=p.isNan
 localret,exists={},{}
 for_,vinipairs(arr)do
 ifisNan(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
 ifnotexists[v]then
 ret[#ret+1]=v
 exists[v]=true
 end
 end
 end
 returnret
 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.
 ------------------------------------------------------------------------------------
 functionp.numKeys(t)
 checkType('numKeys',1,t,'table')
 localisPositiveInteger=p.isPositiveInteger
 localnums={}
 forkinpairs(t)do
 ifisPositiveInteger(k)then
 nums[#nums+1]=k
 end
 end
 table.sort(nums)
 returnnums
 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}.
 ------------------------------------------------------------------------------------
 functionp.affixNums(t,prefix,suffix)
 checkType('affixNums',1,t,'table')
 checkType('affixNums',2,prefix,'string',true)
 checkType('affixNums',3,suffix,'string',true)

 localfunctioncleanPattern(s)
 -- Cleans a pattern so that the magic characters ()%.[]*+-?^$ are interpreted literally.
 returns:gsub('([%(%)%%%.%[%]%*%+%-%?%^%$])','%%%1')
 end

 prefix=prefixor''
 suffix=suffixor''
 prefix=cleanPattern(prefix)
 suffix=cleanPattern(suffix)
 localpattern='^'..prefix..'([1-9]%d*)'..suffix..'$'

 localnums={}
 forkinpairs(t)do
 iftype(k)=='string'then
 localnum=mw.ustring.match(k,pattern)
 ifnumthen
 nums[#nums+1]=tonumber(num)
 end
 end
 end
 table.sort(nums)
 returnnums
 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.
 ------------------------------------------------------------------------------------
 functionp.numData(t,compress)
 checkType('numData',1,t,'table')
 checkType('numData',2,compress,'boolean',true)
 localret={}
 fork,vinpairs(t)do
 localprefix,num=mw.ustring.match(tostring(k),'^([^0-9]*)([1-9][0-9]*)$')
 ifnumthen
 num=tonumber(num)
 localsubtable=ret[num]or{}
 ifprefix==''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
 localsubtable=ret.otheror{}
 subtable[k]=v
 ret.other=subtable
 end
 end
 ifcompressthen
 localother=ret.other
 ret=p.compressSparseArray(ret)
 ret.other=other
 end
 returnret
 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.
 ------------------------------------------------------------------------------------
 functionp.compressSparseArray(t)
 checkType('compressSparseArray',1,t,'table')
 localret={}
 localnums=p.numKeys(t)
 for_,numinipairs(nums)do
 ret[#ret+1]=t[num]
 end
 returnret
 end

 ------------------------------------------------------------------------------------
 -- sparseIpairs
 --
 -- This is an iterator for sparse arrays. It can be used like ipairs, but can
 -- handle nil values.
 ------------------------------------------------------------------------------------
 functionp.sparseIpairs(t)
 checkType('sparseIpairs',1,t,'table')
 localnums=p.numKeys(t)
 locali=0
 locallim=#nums
 returnfunction()
 i=i+1
 ifi<=limthen
 localkey=nums[i]
 returnkey,t[key]
 else
 returnnil,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.
 ------------------------------------------------------------------------------------
 functionp.size(t)
 checkType('size',1,t,'table')
 locali=0
 for_inpairs(t)do
 i=i+1
 end
 returni
 end

 localfunctiondefaultKeySort(item1,item2)
 -- "number" < "string", so numbers will be sorted before strings.
 localtype1,type2=type(item1),type(item2)
 iftype1~=type2then
 returntype1<type2
 elseiftype1=='table'ortype1=='boolean'ortype1=='function'then
 returntostring(item1)<tostring(item2)
 else
 returnitem1<item2
 end
 end
 ------------------------------------------------------------------------------------
 -- keysToList
 --
 -- Returns an array of the keys in a table, sorted using either a default
 -- comparison function or a custom keySort function.
 ------------------------------------------------------------------------------------
 functionp.keysToList(t,keySort,checked)
 ifnotcheckedthen
 checkType('keysToList',1,t,'table')
 checkTypeMulti('keysToList',2,keySort,{'function','boolean','nil'})
 end

 localarr={}
 localindex=1
 forkinpairs(t)do
 arr[index]=k
 index=index+1
 end

 ifkeySort~=falsethen
 keySort=type(keySort)=='function'andkeySortordefaultKeySort
 table.sort(arr,keySort)
 end

 returnarr
 end

 ------------------------------------------------------------------------------------
 -- sortedPairs
 --
 -- Iterates through a table, with the keys sorted using the keysToList function.
 -- If there are only numerical keys, sparseIpairs is probably more efficient.
 ------------------------------------------------------------------------------------
 functionp.sortedPairs(t,keySort)
 checkType('sortedPairs',1,t,'table')
 checkType('sortedPairs',2,keySort,'function',true)

 localarr=p.keysToList(t,keySort,true)

 locali=0
 returnfunction()
 i=i+1
 localkey=arr[i]
 ifkey~=nilthen
 returnkey,t[key]
 else
 returnnil,nil
 end
 end
 end

 ------------------------------------------------------------------------------------
 -- isArray
 --
 -- Returns true if the given value is a table and all keys are consecutive
 -- integers starting at 1.
 ------------------------------------------------------------------------------------
 functionp.isArray(v)
 iftype(v)~='table'then
 returnfalse
 end
 locali=0
 for_inpairs(v)do
 i=i+1
 ifv[i]==nilthen
 returnfalse
 end
 end
 returntrue
 end

 ------------------------------------------------------------------------------------
 -- isArrayLike
 --
 -- Returns true if the given value is iterable and all keys are consecutive
 -- integers starting at 1.
 ------------------------------------------------------------------------------------
 functionp.isArrayLike(v)
 ifnotpcall(pairs,v)then
 returnfalse
 end
 locali=0
 for_inpairs(v)do
 i=i+1
 ifv[i]==nilthen
 returnfalse
 end
 end
 returntrue
 end

 ------------------------------------------------------------------------------------
 -- invert
 --
 -- Transposes the keys and values in an array. For example, {"a", "b", "c"} ->
 -- {a = 1, b = 2, c = 3}. Duplicates are not supported (result values refer to
 -- the index of the last duplicate) and NaN values are ignored.
 ------------------------------------------------------------------------------------
 functionp.invert(arr)
 checkType("invert",1,arr,"table")
 localisNan=p.isNan
 localmap={}
 fori,vinipairs(arr)do
 ifnotisNan(v)then
 map[v]=i
 end
 end

 returnmap
 end

 ------------------------------------------------------------------------------------
 -- listToSet
 --
 -- Creates a set from the array part of the table. Indexing the set by any of the
 -- values of the array returns true. For example, {"a", "b", "c"} ->
 -- {a = true, b = true, c = true}. NaN values are ignored as Lua considers them
 -- never equal to any value (including other NaNs or even themselves).
 ------------------------------------------------------------------------------------
 functionp.listToSet(arr)
 checkType("listToSet",1,arr,"table")
 localisNan=p.isNan
 localset={}
 for_,vinipairs(arr)do
 ifnotisNan(v)then
 set[v]=true
 end
 end

 returnset
 end

 ------------------------------------------------------------------------------------
 -- deepCopy
 --
 -- Recursive deep copy function. Preserves identities of subtables.
 ------------------------------------------------------------------------------------
 localfunction_deepCopy(orig,includeMetatable,already_seen)
 -- Stores copies of tables indexed by the original table.
 already_seen=already_seenor{}

 localcopy=already_seen[orig]
 ifcopy~=nilthen
 returncopy
 end

 iftype(orig)=='table'then
 copy={}
 fororig_key,orig_valueinpairs(orig)do
 copy[_deepCopy(orig_key,includeMetatable,already_seen)]=_deepCopy(orig_value,includeMetatable,already_seen)
 end
 already_seen[orig]=copy

 ifincludeMetatablethen
 localmt=getmetatable(orig)
 ifmt~=nilthen
 localmt_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
 returncopy
 end

 functionp.deepCopy(orig,noMetatable,already_seen)
 checkType("deepCopy",3,already_seen,"table",true)
 return_deepCopy(orig,notnoMetatable,already_seen)
 end

 ------------------------------------------------------------------------------------
 -- sparseConcat
 --
 -- 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"
 ------------------------------------------------------------------------------------
 functionp.sparseConcat(t,sep,i,j)
 localarr={}

 localarr_i=0
 for_,vinp.sparseIpairs(t)do
 arr_i=arr_i+1
 arr[arr_i]=v
 end

 returntable.concat(arr,sep,i,j)
 end

 ------------------------------------------------------------------------------------
 -- length
 --
 -- Finds the length of an array, or of a quasi-array with keys such as "data1",
 -- "data2", etc., using an exponential 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.
 ------------------------------------------------------------------------------------
 functionp.length(t,prefix)
 -- requiring module inline so that [[Module:Exponential search]] which is
 -- only needed by this one function doesn't get millions of transclusions
 localexpSearch=require("Module:Exponential search")
 checkType('length',1,t,'table')
 checkType('length',2,prefix,'string',true)
 returnexpSearch(function(i)
 localkey
 ifprefixthen
 key=prefix..tostring(i)
 else
 key=i
 end
 returnt[key]~=nil
 end)or0
 end

 ------------------------------------------------------------------------------------
 -- inArray
 --
 -- Returns true if valueToFind is a member of the array, and false otherwise.
 ------------------------------------------------------------------------------------
 functionp.inArray(arr,valueToFind)
 checkType("inArray",1,arr,"table")
 -- if valueToFind is nil, error?

 for_,vinipairs(arr)do
 ifv==valueToFindthen
 returntrue
 end
 end
 returnfalse
 end

 returnp

AltStyle によって変換されたページ (->オリジナル) /