Table functions - Yonaba/Moses GitHub Wiki
Table functions
local M = require 'moses'
clear (t)
Clears a table. All its values becomes nil. Returns the passed-in table.
M.clear({1,2,'hello',true}) -- => {}
each (t, f)
Aliases: forEach
.
Iterates over each value-key pair in the passed-in table.
M.each({4,2,1},print)
-- => 4 1
-- => 2 2
-- => 1 3
The table can be map-like (both array part and hash part).
M.each({one = 1, two = 2, three = 3},print)
-- => 1 one
-- => 2 two
-- => 3 three
Can index and assign in an outer table or in the passed-in table:
t = {'a','b','c'}
M.each(t,function(v,i)
t[i] = v:rep(2)
print(t[i])
end)
-- => aa
-- => bb
-- => cc
eachi (t, f)
Aliases: forEachi
.
Iterates only on integer keys in an array table. It returns value-key pairs.
M.eachi({4,2,1},print)
-- => 4 1
-- => 2 2
-- => 1 3
The given array can be sparse, or even have a hash-like part.
local t = {a = 1, b = 2, [0] = 1, [-1] = 6, 3, x = 4, 5}
M.eachi(t,print)
-- => 6 -1
-- => 1 0
-- => 3 1
-- => 5 2
at (t, ...)
Collects values at given keys and returns them in an array.
local t = {4,5,6}
M.at(t,1,3) -- => "{4,6}"
local t = {a = 4, bb = true, ccc = false}
M.at(t,'a', 'ccc') -- => "{4, false}"
adjust (t, key, f)
Adjusts the value at a given key using a function or a value. In case f
is a function, it should be prototyped f(v)
.
It does not mutate the given table, but rather returns a new array.
local t = {1,2,3}
M.adjust(t, 2, math.sin) -- => {1, 0.90929, 3}
local v = {x = 1}
M.adjust(t, 'x', 4) -- => {x = 4}
In case the given key
does not exist in t
, it throws an error.
count (t [, val])
Counts the number of occurences of a given value in a table.
M.count({1,1,2,3,3,3,2,4,3,2},1) -- => 2
M.count({1,1,2,3,3,3,2,4,3,2},2) -- => 3
M.count({1,1,2,3,3,3,2,4,3,2},3) -- => 4
M.count({false, false, true},false) -- => 2
M.count({false, false, true},true) -- => 1
Returns the size of the list in case no value was provided.
M.count({1,1,2,3,3}) -- => 5
countf (t, f)
Counts the number of values passing an iterator test.
M.countf({1,2,3,4,5,6}, function(v)
return v%2==0
end) -- => 3
M.countf({print, pairs, os, assert, ipairs}, function(v)
return type(v)=='function'
end) -- => 4
allEqual (t [, comp])
Aliases: alleq
.
Checks if all values in a collection are equal. Uses M.isEqual
by default to compare values.
M.allEqual({1,1,1,1,1}, comp) -- => true
M.allEqual({1,1,2,1,1}, comp) -- => false
local t1 = {1, 2, {3}}
local t2 = {1, 2, {3}}
M.allEqual({t1, t2}) -- => true
Can take an optional comp
function which will be used to compare values.
local t1 = {x = 1, y = 0}
local t2 = {x = 1, y = 0}
local t3 = {x = 1, y = 2}
local t4 = {x = 1, y = 2}
local function compx(a, b) return a.x == b.x end
local function compy(a, b) return a.y == b.y end
M.allEqual({t1, t2}, compx) -- => true
M.allEqual({t1, t2}, compy) -- => true
M.allEqual({t3, t4}, compx) -- => true
M.allEqual({t3, t4}, compy) -- => true
M.allEqual({t1, t2, t3, t4}, compx) -- => true
M.allEqual({t1, t2, t3, t4}, compy) -- => false
cycle (t [, n = 1])
Aliases: loop
.
Returns a function which iterates on each value-key pair in a given table (similarly to M.each
), except that it restarts iterating again n
times.
If n
is not provided, it defaults to 1.
local t = {'a','b','c'}
for v in M.cycle(t, 2) do
print(v)
end
-- => 'a'
-- => 'b'
-- => 'c'
-- => 'a'
-- => 'b'
-- => 'c'
Supports array-like tables and map-like tables.
local t = {x = 1, y = 2, z = 3}
for v in M.cycle(t) do
print(v)
end
-- => 2
-- => 1
-- => 3
map (t, f)
Aliases: collect
.
Executes a function on each value in a given array.
M.map({1,2,3},function(v)
return v+10
end) -- => "{11,12,13}"
M.map({a = 1, b = 2},function(v, k)
return k..v
end) -- => "{a = 'a1', b = 'b2'}"
It also maps both keys and values.
M.map({a = 1, b = 2},function(v, k)
return k..k, v*2
end) -- => "{aa = 2, bb = 4}"
reduce (t, f [, state = next(t)])
Aliases: inject
, foldl
.
Can sum all values in a table. In case state
is not provided, it defaults to the first value in the given table t
.
local function add(a,b) return a+b end
M.reduce({1,2,3,4},add) -- => 10
Or concatenates all values.
local function concat(a,b) return a..b end
M.reduce({'a','b','c','d'},concat) -- => abcd
best (t, f)
Returns the best value passing a selector function. Acts as a special case of reduce
, using the first value in t
as
an initial state. It thens folds the given table, testing each of its values v
and selecting the value passing the
call f(state,v)
every time.
local words = {'Lua', 'Programming', 'Language'}
M.best(words, function(a,b) return #a > #b end) -- => 'Programming'
M.best(words, function(a,b) return #a < #b end) -- => 'Lua'
reduceBy (t, f, pred [, state = next(t)])
Reduces a table considering only values matching a predicate. For example,let us define a set of values.
local val = {-1, 8, 0, -6, 3, -1, 7, 1, -9}
And a reduction function which will add up values.
local function add(a,b) return a+b end
We can also define some predicate functions.
-- predicate for negative values
local function neg(v) return v<=0 end
-- predicate for positive values
local function pos(v) return v>=0 end
Then we can perform reduction considering only negative or positive values :
M.reduceBy(val, add, neg) -- => -17
M.reduceBy(val, add, pos) -- => 19
An initial state can be passed in.
M.reduceBy(val, add, neg, 17) -- => 0
M.reduceBy(val, add, pos, -19) -- => 0
reduceRight (t, f [, state = next(t)])
Aliases: injectr
, foldr
.
Similar to M.reduce
, but performs from right to left.
local initial_state = 256
local function div(a,b) return a/b end
M.reduceRight({1,2,4,16},div,initial_state) -- => 2
mapReduce (t, f [, state = next(t)])
Aliases: mapr
.
Reduces while saving intermediate states.
local function concat(a,b) return a..b end
M.mapReduce({'a','b','c'},concat) -- => "{'a', 'ab', 'abc'}"
mapReduceRight (t, f [, state = next(t)])
Aliases: maprr
.
Reduces from right to left, while saving intermediate states.
local function concat(a,b) return a..b end
M.mapReduceRight({'a','b','c'},concat) -- => "{'c', 'cb', 'cba'}"
include (t, value)
Aliases: any
, some
, contains
.
Looks for a value in a table.
M.include({6,8,10,16,29},16) -- => true
M.include({6,8,10,16,29},1) -- => false
local complex_table = {18,{2,{3}}}
local collection = {6,{18,{2,6}},10,{18,{2,{3}}},29}
M.include(collection, complex_table) -- => true
Handles iterator functions.
local function isUpper(v) return v:upper()== v end
M.include({'a','B','c'},isUpper) -- => true
detect (t, value)
Returns the index of a value in a table.
M.detect({6,8,10,16},8) -- => 2
M.detect({nil,true,0,true,true},false) -- => nil
local complex_table = {18,{2,6}}
local collection = {6,{18,{2,6}},10,{18,{2,{3}}},29}
M.detect(collection, complex_table) -- => 2
Handles iterator functions.
local function isUpper(v)
return v:upper()==v
end
M.detect({'a','B','c'},isUpper) -- => 2
where (t, props)
Looks through a table and returns all the values that matches all of the key-value pairs listed in props
.
local items = {
{height = 10, weight = 8, price = 500},
{height = 10, weight = 15, price = 700},
{height = 15, weight = 15, price = 3000},
{height = 10, weight = 8, price = 3000},
}
M.where(items, {height = 10}) -- => {items[1], items[2], items[4]}
M.where(items, {weight = 15}) -- => {items[2], items[3]}
M.where(items, {prince = 3000}) -- => {items[3], items[4]}
M.where(items, {height = 10, weight = 15, prince = 700}) -- => {items[2]}
findWhere (t, props)
Looks through a table and returns the first value found that matches all of the key-value pairs listed in props
.
local a = {a = 1, b = 2, c = 3}
local b = {a = 2, b = 3, d = 4}
local c = {a = 3, b = 4, e = 5}
M.findWhere({a, b, c}, {a = 3, b = 4}) == c -- => true
select (t, f)
Aliases: filter
.
Collects values passing a validation test.
local function isEven(v) return v%2==0 end
local function isOdd(v) return v%2~=0 end
M.select({1,2,3,4,5,6,7}, isEven) -- => "{2,4,6}"
M.select({1,2,3,4,5,6,7}, isOdd) -- => "{1,3,5,7}"
reject (t, f)
Aliases: reject
.
Removes all values failing (returning false or nil) a validation test:
local function isEven(v) return v%2==0 end
local function isOdd(v) return v%2~=0 end
M.reject({1,2,3,4,5,6,7}, isEven) -- => "{1,3,5,7}"
M.reject({1,2,3,4,5,6,7}, isOdd) -- => "{2,4,6}"
all (t, f)
Aliases: every
.
Checks whether or not all elements pass a validation test.
local function isEven(v) return v%2==0 end
M.all({2,4,6}, isEven) -- => true
invoke (t, method)
Invokes a given function on each value in a table.
M.invoke({'a','bea','cdhza'},string.len) -- => "{1,3,5}"
Can reference the method of the same name in each value.
local a, b, c, d = {id = 'a'}, {id = 'b'}, {id = 'c'}, {id = 'd'}
local function call(self) return self.id end
M.invoke({a,b,c,d},call) -- => "{'a','b','c','d'}"
pluck (t, property)
Fetches all values indexed with specific key in a table of objects.
local peoples = {
{name = 'John', age = 23},{name = 'Peter', age = 17},
{name = 'Steve', age = 15},{age = 33}}
M.pluck(peoples,'age') -- => "{23,17,15,33}"
M.pluck(peoples,'name') -- => "{'John', 'Peter', 'Steve'}"
max (t [, transform])
Returns the maximum value in a collection.
M.max {1,2,3} -- => 3
M.max {'a','b','c'} -- => 'c'
Can take an iterator function to extract a specific property.
local peoples = {
{name = 'John', age = 23},{name = 'Peter', age = 17},
{name = 'Steve', age = 15},{age = 33}}
M.max(peoples,function(people) return people.age end) -- => 33
min (t [, transform])
Returns the minimum value in a collection.
M.min {1,2,3} -- => 1
M.min {'a','b','c'} -- => 'a'
Can take an iterator function to extract a specific property.
local peoples = {
{name = 'John', age = 23},{name = 'Peter', age = 17},
{name = 'Steve', age = 15},{age = 33}}
M.min(peoples,function(people) return people.age end) -- => 15
same (a, b)
Tests whether or not all values in each of the passed-in tables exists in both tables.
local a = {'a','b','c','d'}
local b = {'b','a','d','c'}
M.same(a,b) -- => true
b[#b+1] = 'e'
M.same(a,b) -- => false
sort (t [, comp = math.min])
Sorts a collection.
M.sort({'b','a','d','c'}) -- => "{'a','b','c','d'}"
Handles custom comparison functions.
M.sort({'b','a','d','c'}, function(a,b)
return a:byte() > b:byte()
end) -- => "{'d','c','b','a'}"
sortedk (t [, comp])
Iterates on values with respect to key order. Keys are sorted using comp
function which defaults to math.min
.
It returns upon each call a key, value
pair.
local tbl = {}; tbl[3] = 5 ; tbl[2] = 6; tbl[5] = 8; tbl[4] = 10; tbl[1] = 12
for k, v in M.sortedk(tbl) do print(k, v) end
-- => 1 12
-- => 2 6
-- => 3 5
-- => 4 10
-- => 5 8
local function comp(a,b) return a > b end
for k, v in M.sortedk(tbl, comp) do print(k, v) end
-- => 5 8
-- => 4 10
-- => 3 5
-- => 2 6
-- => 1 12
sortedv (t [, comp])
Iterates on values with respect to key order. Keys are sorted using comp
function which defaults to math.min
.
It returns upon each call a key, value
pair.
local tbl = {}; tbl[3] = 5 ; tbl[2] = 6; tbl[5] = 8; tbl[4] = 10; tbl[1] = 12
for k, v in M.sortedv(tbl) do print(k, v) end
-- => 3 5
-- => 2 6
-- => 5 8
-- => 4 10
-- => 1 12
local function comp(a,b) return a > b end
for k, v in M.sortedv(tbl, comp) do print(k, v) end
-- => 1 12
-- => 4 10
-- => 5 8
-- => 2 6
-- => 3 5
sortBy (t [, transform [, comp = math.min]])
Sorts items in a collection based on the result of running a transform function through every item in the collection.
local r = M.sortBy({1,2,3,4,5}, math.sin)
print(table.concat(r,','))
-- => {5,4,3,1,2}
The transform function can also be a string name property.
local people = {
{name = 'albert', age = 40},
{name = 'louis', age = 55},
{name = 'steve', age = 35},
{name = 'henry', age = 19},
}
local r = M.sortBy(people, 'age')
M.each(r, function(v) print(v.age, v.name) end)
-- => 19 henry
-- => 35 steve
-- => 40 albert
-- => 55 louis
As seen above, the defaut comparison function is the '<' operator. For example, let us supply a different one to sort the list of people by decreasing age order :
local people = {
{name = 'albert', age = 40},
{name = 'louis', age = 55},
{name = 'steve', age = 35},
{name = 'henry', age = 19},
}
local r = M.sortBy(people, 'age', function(a,b) return a > b end)
M.each(r, function(v) print(v.age, v.name) end)
-- => 55 louis
-- => 40 albert
-- => 35 steve
-- => 19 henry
The transform
function defaults to M.indentity
and in that case, M.sortBy
behaves like M.sort
.
local r = M.sortBy({1,2,3,4,5})
print(table.concat(r,','))
-- => {1,2,3,4,5}
groupBy (t, iter)
Groups values in a collection depending on their return value when passed to a predicate test.
M.groupBy({0,1,2,3,4,5,6},function(v)
return v%2==0 and 'even' or 'odd'
end)
-- => "{odd = {1,3,5}, even = {0,2,4,6}}"
M.groupBy({0,'a',true, false,nil,b,0.5},type)
-- => "{number = {0,0.5}, string = {'a'}, boolean = {true, false}}"
countBy (t, iter)
Splits a table in subsets and provide the count for each subset.
M.countBy({0,1,2,3,4,5,6},function(v)
return v%2==0 and 'even' or 'odd'
end) -- => "{odd = 3, even = 4}"
size (...)
When given a table, provides the count for the very number of values in that table.
M.size {1,2,3} -- => 3
M.size {one = 1, two = 2} -- => 2
When given a vararg list of arguments, returns the count of these arguments.
M.size(1,2,3) -- => 3
M.size('a','b',{}, function() end) -- => 4
containsKeys (t, other)
Checks whether a table has all the keys existing in another table.
M.contains({1,2,3,4},{1,2,3}) -- => true
M.contains({1,2,'d','b'},{1,2,3,5}) -- => true
M.contains({x = 1, y = 2, z = 3},{x = 1, y = 2}) -- => true
sameKeys (tA, tB)
Checks whether both tables features the same keys:
M.sameKeys({1,2,3,4},{1,2,3}) -- => false
M.sameKeys({1,2,'d','b'},{1,2,3,5}) -- => true
M.sameKeys({x = 1, y = 2, z = 3},{x = 1, y = 2}) -- => false