Module Iter
Specialized iteration of tables and other objects.
"pairs" methods take a single input table. "touples" methods take an arbitrary number of tables.
Module Status: Experimental 2020-10-31.
Usage:
local Iter = require('__eradicators-library__/erlib/lua/Iter')()
Todo
todo-1 | Find out how to make LDoc obey custom names for sections and iterators. |
lua.Iter.array_pairs Functions
array_pairs(tbl, i, j) | Iterates over dense, sparse or partial arrays. |
lua.Iter.combinations Functions
combinations(length, arr) | Iterates over all n-length combinations of the given values. |
lua.Iter.dpairs Functions
dpairs([tbl[, opt]]) | Deep_pairs iterates all sub-tables in a single for-loop. |
lua.Iter.fpairs Functions
fpairs(tbl, f) | Returns only (key → value) pairs for which f(value,key,tbl) returns true. |
lua.Iter.fpairs2 Functions
fpairs2(tbl, f) | Iterates the NotNil return values of a filter function over a table. |
lua.Iter.ntuples Functions
ntuples(n[, tbl]) | Iterates n-nested sub-tables in a single for-loop. |
lua.Iter.permutations Functions
permutations([arr[, i=1[, j=#arr]]]) | Iterates through all permutations that contain each value exactly once. |
lua.Iter.sriapi Functions
sriapi(arr) | Inverse ipairs(). |
lua.Iter.subsets Functions
subsets(size, arr) | Produces all possible unique subsets of size n from a given superset arr. |
lua.Iter.sync_tuples Functions
sync_tuples(parent, ...) | Iterates multiple tables in paralell. |
Todo
lua.Iter.array_pairs Functions
- array_pairs(tbl, i, j)
-
Iterates over dense, sparse or partial arrays.
Can also iterate over the array part of a MixedTable.
Start and end point of the array are determind on calling, thus any changes
to the array during the iteration have no effect on the interation range.
Parameters:
- tbl table or array
- i NaturalNumber Start of the iteration range.
- j NaturalNumber End of the iteration range.
Returns:
Usage:
local my_mixed_table = {'a',[42]='u',[6]='f',test='bla'} -- By default it will find the largest key on it's own. for k,v in Iter.array_pairs(my_mixed_table) do print(k,v) end > 1 a > 6 f > 42 u -- You can enforce partial ranges. This *will* iterate the whole range -- even if only a few keys have values. So speficying huge numbers -- will just pointlessly waste CPU cycles. for k,v in Iter.array_pairs(my_mixed_table,6,9001) do print(k,v) end > 6 f > 42 u
lua.Iter.combinations Functions
- combinations(length, arr)
-
Iterates over all n-length combinations of the given values.
Each value can be in the combination multiple times.
Time complexity: Size of arr to the power of length → #arr ^ length
See also: Iter.subsets, Iter.permutations
Parameters:
- length NaturalNumber The exact length of every combination.
- arr DenseArray or SparseArray The values to be combined.
Returns:
-
function
A stateful iterator that returns an array for each
combination.
Usage:
for arr in Iter.combinations(3,{0,1}) do print(Hydra.line(arr)) end > {0, 0, 0} > {0, 0, 1} > {0, 1, 0} > {0, 1, 1} > {1, 0, 0} > {1, 0, 1} > {1, 1, 0} > {1, 1, 1}
lua.Iter.dpairs Functions
- dpairs([tbl[, opt]])
-
Deep_pairs iterates all sub-tables in a single for-loop.
This is a non-fixed-path-length fork of Iter.ntuples.
With default options every (key → value) pair is iterated exactly once. The sub-tables that are being iterated into are never output as the value of such a pair.
Parameters:
- tbl table The input table. (optional)
- opt (table) Options (optional).
- include_path boolean Activates the fourth return value. (default false)
- include_duplicates boolean When this is false and @tbl has multiple references to the same sub-table, then only the first reference to each sub-table will be iterated and all futher reference skipped. When this is true all references will be iterated normally and the fifth return value will be true for all iterations except the first. (default false)
- ignore_recursion boolean Silently skip recursive sub-tables instead of raising an error. (default false)
Returns:
-
NotNil
key
-
NotNil
value
-
table
table
This is the sub-table that contains the returned (key → value) pair. -
TablePath or nil
path
This is the full path to the value inside the input table @tbl. -
boolean or nil
is_duplicate
Usage:
-- Setup the example data. local players = { yurie = { items = {'iron-plate', 'copper-plate'}, ammo = {'piercing' , 'uranium' }, play_time = 9001, }, tarou = { items = {'iron-gear' , 'copper-gear' }, ammo = {}, play_time = 42, }, akira = { items = {}, ammo = {'exploding' , 'magic' }, play_time = 7, }, } -- Introduce some duplicate table references. players.michiru = players.tarou players.akira.ammo['ref'] = players.yurie.ammo -- And an easy to read format. local function format_row(key, value, path, is_duplicate) return ('%-28s %-10s %-13s %-5s') :format(Hydra.line(path), key, value, is_duplicate and '(duplicate)' or '') end
-- Example 1: Path. (Also notice that duplicates are ignored by default.) for key, value, tbl, path in Iter.dpairs(players, {include_path = true}) do print(format_row(key, value, path)) end --[path] [key] [value] > {"michiru", "items", 1} 1 iron-gear > {"michiru", "items", 2} 2 copper-gear > {"michiru", "play_time"} play_time 42 > {"akira", "ammo", 1} 1 exploding > {"akira", "ammo", 2} 2 magic > {"akira", "play_time"} play_time 7 > {"yurie", "ammo", 1} 1 piercing > {"yurie", "ammo", 2} 2 uranium > {"yurie", "items", 1} 1 iron-plate > {"yurie", "items", 2} 2 copper-plate > {"yurie", "play_time"} play_time 9001
-- Example 2: Duplicates. for key, value, tbl, path, is_duplicate in Iter.dpairs(players, {include_path = true, include_duplicates=true}) do print(format_row(key, value, path, is_duplicate)) end --[path] [key] [value] > {"michiru", "items", 1} 1 iron-gear > {"michiru", "items", 2} 2 copper-gear > {"michiru", "play_time"} play_time 42 > {"akira", "ammo", 1} 1 exploding > {"akira", "ammo", 2} 2 magic > {"akira", "ammo", "ref", 1} 1 piercing -- notice pairs-based > {"akira", "ammo", "ref", 2} 2 uranium -- random order > {"akira", "play_time"} play_time 7 > {"yurie", "ammo", 1} 1 piercing (duplicate) > {"yurie", "ammo", 2} 2 uranium (duplicate) > {"yurie", "items", 1} 1 iron-plate > {"yurie", "items", 2} 2 copper-plate > {"yurie", "play_time"} play_time 9001 > {"tarou", "items", 1} 1 iron-gear (duplicate) > {"tarou", "items", 2} 2 copper-gear (duplicate) > {"tarou", "play_time"} play_time 42 (duplicate)
-- Example 3: Recursion. -- Let's introduce a loop... players.tarou.loop = players -- and try the same command as in Example 2... for key, value, tbl, path, is_duplicate in Iter.dpairs(players, {include_path = true, include_duplicates=true}) do print(format_row(key, value, path, is_duplicate)) end > Error! > Table recursion detected but not allowed. > path: {"michiru", "loop"} -- Notice that "michiru" and "tarou" reference the same sub-table. -- The error message reports whichever path it found first. -- Now let't try again with irgnore mode... for key, value, tbl, path, is_duplicate in Iter.dpairs(players, { include_path = true, include_duplicates=true, ignore_recursion = true }) do print(format_row(key, value, path, is_duplicate)) end > --[[This produces output identical to Example 2.]]
lua.Iter.fpairs Functions
- fpairs(tbl, f)
-
Returns only (key → value) pairs for which f(value,key,tbl) returns true.
Other values in the table are simply skipped during iteration.
Parameters:
Returns:
-
A
stateless iterator function.
Usage:
-- For example if you've got stored LuaEntity references script.on_event(defines.events.on_built_entity,function(event) if event.entity.name == 'MyEntity' then global.MyEntityData[event.entity.unit_number] = {entity=event.entity} end end) -- And want to remove the invalid ones. (Ignores key+tbl arguments) local filter = function(data,_,_) return not data.entity.valid end for unit_number,_ in pairs(global.MyEntityData,filter) do global.MyEntityData[unit_number] = nil end
lua.Iter.fpairs2 Functions
- fpairs2(tbl, f)
-
Iterates the NotNil return values of a filter function over a table.
Parameters:
Returns:
-
function
A stateful iterator function.
For each call of the iterator it returns all return values
of the next call of
'f(value,key,tbl)'
that returns at least one value. The iterator terminates after f has been called on all elemetnts of tbl.
lua.Iter.ntuples Functions
- ntuples(n[, tbl])
-
Iterates n-nested sub-tables in a single for-loop.
Returns every possible path of length n-1 and its value. This might lead to unexpected results when the input table is recursive or has multiple references to the same sub-table.
See also Iter.dpairs.Internally uses pairs() to get each sub-table's __pairs metamethod or next().
See also Metatables and Metamethods.Every sub-table must be at least of depth
n-1
or the loop will crash. Completely empty sub-tables are the only exception and will instead be ignored.Parameters:
- n
NaturalNumber
The length of the desired tuple. The returned tuple
is of the form
("Key 1", "Key 2", ..., "Key n-1", "Value")
, thus the value is included in the length of the touple. - tbl table The parent table to iterate into. (optional)
Returns:
-
function
There are three cases:
When tbl == nil returns Filter.SKIP.
When n <= 2 returns the three values returned by pairs(tbl).
When n > 2 returns a stateful ntuples-iterator function and nothing else.Usage:
local players = { yurie = { items = {'iron-plate', 'copper-plate'}, ammo = {'piercing' , 'uranium' }, }, tarou = { items = {'iron-gear' , 'copper-gear' }, ammo = {}, -- empty table is ignored because it's not deep enough. }, akira = { items = {}, ammo = {'exploding' , 'magic' }, }, } for nickname, type, item_index, item_name in Iter.ntuples(4, players) do print(nickname, type, item_index, item_name) end > yurie items 1 iron-plate > yurie items 2 copper-plate > yurie ammo 1 piercing > yurie ammo 2 uranium > tarou items 1 iron-gear > tarou items 2 copper-gear > akira ammo 1 exploding > akira ammo 2 magic
- n
NaturalNumber
The length of the desired tuple. The returned tuple
is of the form
lua.Iter.permutations Functions
- permutations([arr[, i=1[, j=#arr]]])
-
Iterates through all permutations that contain each value exactly once.
A permutation is a list in which only the order of elements changes.
Time complexity: Factorial of size of arr → #arr!
See also: Iter.combinations, Iter.subsets
Parameters:
- arr DenseArray or SparseArray If not given will instead use a Table.range(i,j). (optional)
- i NaturalNumber The index of the first value that shall be part of the permutation. (default 1)
- j NaturalNumber The index of the last value that shall be part of the permutation. (default #arr)
Returns:
-
function
A stateful iterator that returns an array for each
permutation.
Usage:
for arr in Iter.permutations({1,2,3,4,5},1,3) do print(Hydra.line(arr)) end > {1, 2, 3} > {1, 3, 2} > {3, 1, 2} > {3, 2, 1} > {2, 3, 1} > {2, 1, 3}
lua.Iter.sriapi Functions
- sriapi(arr)
-
Inverse ipairs().
Parameters:
- arr DenseArray
Returns:
-
function
f() A stateful iterator. When the iterator is called f()
it returns (value, index) of the next entry in the array. Note:
this means that the order of the return values is also inversed
compared to normal ipairs().
The
__ipairs
methamethod of arr is ignored by this iterator.Usage:
for v,i in sriapi({'a','b','c','d','e'}) do print(('i=%s, v=%s'):format(i,v)) end > i=5, v=e > i=4, v=d > i=3, v=c > i=2, v=b > i=1, v=a
lua.Iter.subsets Functions
- subsets(size, arr)
-
Produces all possible unique subsets of size n from a given superset arr.
This means that it is guaranteed that for each possible combination of
unique elements exactly one permutation will occur.
Time complexity: It depends *cough*.
Note: Despite the name sub-"set" all in- and outputs use array format for better performance.
See also: Iter.combinations, Iter.permutations
Parameters:
- size NaturalNumber The exact size of each subset.
- arr array An array representing all elements of the source set.
Returns:
-
function
A stateful iterator that returns an array for each
subset.
Usage:
-- A set with n elements only has one subset of length n. for s in Iter.subsets(3,{1,2,3}) do print(Hydra.line(s)) end > {1, 2, 3}
for s in Iter.subsets(3,{1,2,3,4,5}) do print(Hydra.line(s)) end > {1, 2, 3} > {1, 2, 4} > {1, 2, 5} > {1, 3, 4} > {1, 3, 5} > {1, 4, 5} > {2, 3, 4} > {2, 3, 5} > {2, 4, 5} > {3, 4, 5}
lua.Iter.sync_tuples Functions
- sync_tuples(parent, ...)
-
Iterates multiple tables in paralell.
Note: Standard next rules apply regarding in-loop manipulation of the parent table.
Note: If the parents has a __pairs metamethod its resulting keys are used for all child tables.
Parameters:
- parent table This determines which keys are iterated.
- ... table or nil Additional tables to be iterated.
Returns:
-
k,v1,v2,v3,... Each iteration return the key and the value for each
table in the order the tables were given.
Usage:
-- Keys not in the parent table won't be iterated. local par = {nil, 'b', 'c', 'd', false, ['x'] = 'f'} -- Other tables nil keys will just return nil. local t1 = {'A', 'B', 'C', nil, 'E', ['x'] = 'F'} local t2 = {nil, nil, 3 , 4 , 5 , ['x'] = 6 } -- Empty arguments are assumed to be empty tables. local t3 = nil for k,vp,v1,v2,v3 in Iter.sync_tuples(par,t1,t2,t3) do print(k,':',vp,v1,v2,v3) end > 2 : b B nil nil > 3 : c C 3 nil > 4 : d nil 4 nil > 5 : false E 5 nil > x : f F 6 nil