diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..1dcbdbd
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,35 @@
+name: CI
+
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ luaVersion: ["5.4"]
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - uses: leafo/gh-actions-lua@v9
+ with:
+ luaVersion: ${{ matrix.luaVersion }}
+
+ - uses: leafo/gh-actions-luarocks@v4
+
+ - name: Install dependencies
+ run: luarocks install simple_test
+
+ - name: Run unit tests
+ run: |
+ LUA_PATH="./src/?.lua;./src/?/init.lua;;$(luarocks path --lr-path)" lua test.lua
diff --git a/.gitignore b/.gitignore
index 5334d64..bf547dc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
*.rock
+*.rockspec
.luarocks_key
diff --git a/.travis.yml b/.travis.yml
index ce49346..75d3ce0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,11 +12,10 @@ before_install:
- export PATH=$PATH:$PWD/lua_install/bin
install:
- - luarocks install luautf8
- luarocks install simple_test
script:
- - lua ./test.lua
+ - make test
notifications:
email:
diff --git a/Makefile b/Makefile
index 12ee6be..e8ddd6b 100644
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,7 @@ install_dependencies:
luarocks install simple_test
test:
- lua test.lua
+ LUA_PATH="./src/?.lua;./src/?/init.lua;./src/array/?.lua;;" lua test.lua
rockspec:
bash $(task_folder)create_rockspec.sh $(version)
diff --git a/README.md b/README.md
index dee013a..0f4813b 100644
--- a/README.md
+++ b/README.md
@@ -1,92 +1,12 @@
# array.lua
-A small library with useful methods to handle Lua's table when it's working like an Array
-
-[](https://travis-ci.org/EvandroLG/array.lua)
+A small library with useful methods to handle Lua's table when it's working like an Array.
+For docs, see: https://evandrolg.github.io/array.lua/
## Installation
To install array, run:
```sh
$ luarocks install array
```
-Or simply copy the array.lua file and paste in your project.
-
-## Methods
-* array.is_array(object:table):boolean
-Checks if table is an Array
-
-* array.is_empty(object:table):boolean
-Checks if table is empty
-
-* array.slice(object:table, start:number, end:number):table
-Returns a shallow copy of a portion of a table into a new table
-
-* array.index_of(object:table, value:*):number
-Returns the index at which value can be found or -1 if value is not present
-
-* array.reverse(object:table):table
-Creates a new table with reverse values
-
-* array.first(object:table):*
-Returns the first value in a table
-
-* array.last(object:table):*
-Returns the last value in a table
-
-* array.max(object:table):*
-Returns the maximum value in a table
-
-* array.min(object:table):*
-Returns the minimum value in a table
-
-* array.map(object:table, callback:function):table
-Creates a new table of values by mapping each value in list through a transformation function
-
-* array.filter(object:table, callback:function):table
-Produces a new table containing all elements that pass truth test
-
-* array.reduce(object:table, callback:function [, memo]):*
-Applies a function against an accumulator and each value of the table to reduce it to a single value
-
-* array.reduce_right(object:table, callback:function [, memo]):*
-Works like `reduce` except that it iterates over table's elements from right to left
-
-* array.sum(object:table):number
-Returns the sum of the values of the table passed by parameter
-
-* array.concat(object:table, object:table):table
-Returns a new table by joining all values from the two tables
-
-* array.uniq(object:table):table
-Returns a new table by removing duplicates values
-
-* array.without(object:table, object:table):table
-Returns a copy of the table with all instances of the values removed
-
-* array.some(object:table, callback:function):boolean
-Tests whether at least one element in the table passes the test implemented by the callback
-
-* array.every(object:table, callback:function):boolean
-Tests whether all elements in the table passes the test implemented by the callback
-
-* array.zip(object:table, object:table):table
-Returns a table of the two supplied by pairing up equally-positioned elements from both tables
-
-* array.shallow_copy(object:table):table
-Returns a shallow copy of the table passed as parameter
-
-* array.deep_copy(object:table):table
-Returns a deep copy of the table passed as parameter
-
-* array.diff(object:table, object:table):table
-Returns a new table with the items which exist only in the first table
-
-* array.flat(object:table):table
-Creates a new table with the sub-table elements concatenated into it
-
-* array.fill(value:*, [start:number], end:number):table
-Creates a table filling all the elements from a start index (default
-one) to an end index with a default value passed by parameter.
-* array.remove(object:table, callback:function):table
-Removes all elements from table that `callback` returns truthy for and returns a new table with the removed elements
+## License
+[MIT](https://github.com/EvandroLG/array.lua/tree/master/LICENSE)
diff --git a/array.lua b/array.lua
deleted file mode 100644
index 86006ec..0000000
--- a/array.lua
+++ /dev/null
@@ -1,497 +0,0 @@
--- array
--- author: Evandro Leopoldino Gonçalves
--- https://github.com/evandrolg
--- License: MIT
-
--- Helper function to check if value passed by parameter is a table
--- @obj {table}
--- @returns {boolean}
-local function is_table(obj)
- return type(obj) == 'table'
-end
-
--- Raises error if @param is not an array
--- @obj {table}
--- @param {table}
--- @method {string}
--- @returns {void}
-local function raises_error(obj, param, method)
- assert(obj.is_array(param), string.format('%s expects an array', method))
-end
-
-local array
-
-array = {
- __VERSION = '1.2.6',
- __DESCRIPTION = "A small library with useful methods to handle Lua's table when it's working like an Array",
- __LICENCE = [[
- The MIT License (MIT)
- Copyright (c) 2017 Evandro Leopoldino Gonçalves
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
- ]],
-
- -- Verify if table object works as an array
- -- @obj {table}
- -- @returns {boolean}
- is_array = function(obj)
- if not is_table(obj) then return false end
-
- local i = 0
- for _ in pairs(obj) do
- i = i + 1
- if obj[i] == nil then return false end
- end
-
- return true
- end,
-
- -- Check if parameter is an empty table
- -- @obj {table}
- -- @returns {boolean}
- is_empty = function(obj)
- return array.is_array(obj) and #obj == 0
- end,
-
- -- Return a shallow copy of a portion of a table into a new table
- -- @obj {table}
- -- @start {number} start value
- -- @finish {number} end value
- -- @returns {boolean}
- slice = function(obj, start, finish)
- raises_error(array, obj, 'slice')
-
- if array.is_empty(obj) then return {} end
-
- local _start = start or 1
-
- local output = {}
- for i=_start, finish or #obj do
- table.insert(output, obj[i])
- end
-
- return output
- end,
-
- -- Return the index at which value can be found or -1 in case value is not present
- -- @obj {table}
- -- @value {*}
- -- @returns {boolean}
- index_of = function(obj, value)
- raises_error(array, obj, 'index_of')
-
- for i=1, #obj do
- if obj[i] == value then
- return i
- end
- end
-
- return -1
- end,
-
- -- Create a new table with reverse values
- -- @obj {table}
- -- @returns {table}
- reverse = function(obj)
- raises_error(array, obj, 'reverse')
-
- local output = {}
-
- for i=#obj, 1, -1 do
- table.insert(output, obj[i])
- end
-
- return output
- end,
-
- -- Return first element from the table
- -- @obj {table}
- -- @returns {*}
- first = function(obj)
- raises_error(array, obj, 'first')
- return obj[1]
- end,
-
- -- Return last element from the table
- -- @obj {table}
- -- @returns {*}
- last = function(obj)
- raises_error(array, obj, 'last')
- return obj[#obj]
- end,
-
- -- Return maximum value from the table
- -- @obj {table}
- -- @returns {*}
- max = function(obj)
- raises_error(array, obj, 'max')
-
- local max = obj[1]
-
- for i=2, #obj do
- if obj[i] > max then
- max = obj[i]
- end
- end
-
- return max
- end,
-
- -- Return minimum value from the table
- -- @obj {table}
- -- @returns {*}
- min = function(obj)
- raises_error(array, obj, 'min')
-
- local min = obj[1]
-
- for i=2, #obj do
- if obj[i] < min then
- min = obj[i]
- end
- end
-
- return min
- end,
-
- -- Create a new table of values by mapping each value in table through a transformation function
- -- @obj {table}
- -- @callback {function}
- -- @returns {*}
- map = function(obj, callback)
- raises_error(array, obj, 'map')
-
- local output = {}
-
- for i=1, #obj do
- table.insert(output, callback(obj[i], i))
- end
-
- return output
- end,
-
- -- Create a new table containing all elements that pass truth test
- -- @obj {table}
- -- @callback {function}
- -- @returns {*}
- filter = function(obj, callback)
- raises_error(array, obj, 'filter')
-
- local output = {}
-
- for i=1, #obj do
- if callback(obj[i], i) then
- table.insert(output, obj[i])
- end
- end
-
- return output
- end,
-
- -- Applies a function against an accumulator and each value of the table to reduce it to a single value
- -- @obj {table}
- -- @callback {function}
- -- @memo {*}
- -- @returns {*}
- reduce = function(obj, callback, memo)
- raises_error(array, obj, 'reduce')
-
- local initialIndex = 1
- local _memo = memo
-
- if _memo == nil then
- initialIndex = 2
- _memo = obj[1]
- end
-
- for i=initialIndex, #obj do
- _memo = callback(_memo, obj[i], i)
- end
-
- return _memo
- end,
-
- -- This function is like reduce except that it interates over table's elements from right to left
- -- @obj {table}
- -- @callback {function}
- -- @memo {*}
- -- @returns {*}
- reduce_right = function(obj, callback, memo)
- raises_error(array, obj, 'reduce_right')
-
- local initialIndex = #obj
- local _memo = memo
-
- if _memo == nil then
- initialIndex = initialIndex - 1
- _memo = obj[#obj]
- end
-
- for i=initialIndex, 1, -1 do
- _memo = callback(_memo, obj[i], i)
- end
-
- return _memo
- end,
-
- -- Return the sum of the values in table
- -- @obj {table}
- -- @callback {function}
- -- @returns {number}
- sum = function(obj)
- raises_error(array, obj, 'sum')
-
- return array.reduce(obj, function(memo, value)
- return memo + value
- end)
- end,
-
- -- Return a new table joining all values from the two tables passed by parameter
- -- @obj {table}
- -- @obj2 {table}
- -- @returns {table}
- concat = function(obj, obj2)
- raises_error(array, obj, 'concat')
- raises_error(array, obj2, 'concat')
-
- local output = {}
-
- for i=1, #obj do
- table.insert(output, obj[i])
- end
-
- for i=1, #obj2 do
- table.insert(output, obj2[i])
- end
-
- return output
- end,
-
- -- Create a new table, removing duplicates values
- -- @obj {table}
- -- @returns {table}
- uniq = function(obj)
- raises_error(array, obj, 'uniq')
-
- local output = {}
- local seen = {}
-
- for i=1, #obj do
- local value = obj[i]
- if not seen[value] then
- seen[value] = true
- table.insert(output, value)
- end
- end
-
- return output
- end,
-
- -- Return a copy of the table with all instances of the values removed
- -- @obj {table}
- -- @values {table}
- -- @returns {table}
- without = function(obj, values)
- raises_error(array, obj, 'without')
-
- local output = {}
-
- for i=1, #obj do
- if array.index_of(values, obj[i]) == -1 then
- table.insert(output, obj[i])
- end
- end
-
- return output
- end,
-
- -- Tests if at least one element in the table passes the test implemented by the callback
- -- @obj {table}
- -- @callback {function}
- -- @returns {boolean}
- some = function(obj, callback)
- raises_error(array, obj, 'some')
-
- for i=1, #obj do
- if callback(obj[i], i) then
- return true
- end
- end
-
- return false
- end,
-
- -- Return a table of the two supplied by pairing up equally-positioned elements from both tables
- -- @obj {table}
- -- @obj2 {table}
- -- @returns {table}
- zip = function(obj1, obj2)
- raises_error(array, obj1, 'zip')
- raises_error(array, obj2, 'zip')
-
- local output = {}
- local size = #obj1 > #obj2 and #obj2 or #obj1
-
- for i=1, size do
- table.insert(output, { obj1[i], obj2[i] })
- end
-
- return output
- end,
-
- -- Return a table of the two supplied by pairing up equally-positioned elements from both tables
- -- @obj {table}
- -- @obj2 {table}
- -- @returns {table}
- every = function(obj, callback)
- raises_error(array, obj, 'every')
-
- for i=1, #obj do
- if not callback(obj[i], i) then
- return false
- end
- end
-
- return true
- end,
-
- -- Returns a shallow copy of the table passed as parameter
- -- @obj {table}
- -- @returns {table}
- shallow_copy = function(obj)
- raises_error(array, obj, 'shallow_copy')
-
- local output = {}
-
- for i=1, #obj do
- table.insert(output, obj[i])
- end
-
- return output
- end,
-
- -- Return a deep copy of the table passed as parameter
- -- @value {*}
- -- @returns {table}
- deep_copy = function(value)
- local output = value
-
- if is_table(value) then
- output = {}
-
- for i=1, #value do
- table.insert(output, array.deep_copy(value[i]))
- end
- end
-
- return output
- end,
-
- -- Return a new table with the items which exist only in the first table
- -- @obj {table}
- -- @obj2 {table}
- -- @returns {table}
- diff = function(obj1, obj2)
- raises_error(array, obj1, 'diff')
- raises_error(array, obj2, 'diff')
-
- local output = {}
-
- for i=1, #obj1 do
- local has_value = false
- local value = obj1[i]
-
- for j=1, #obj2 do
- if value == obj2[j] then
- has_value = true
- end
- end
-
- if not has_value then
- table.insert(output, value)
- end
- end
-
- return output
- end,
-
- -- Create a new table with the sub-table elements concatenated into it
- -- @obj {table}
- -- @_memo {table}
- -- @returns {table}
- flat = function(obj, _memo)
- local output = _memo or {}
-
- for i=1, #obj do
- local value = obj[i]
-
- if is_table(value) then
- array.flat(value, output)
- else
- table.insert(output, value)
- end
- end
-
- return output
- end,
-
- -- Creates a table filling all the elements from a start index (default one) to an end index with a default value
- -- @value {*}
- -- @start_or_finish {number}
- -- @finish {number}
- -- @returns {table}
- fill = function(value, start_or_finish, finish)
- local output = {}
- local item = value
- local start = start_or_finish
- local size = finish
-
- if finish == nil then
- start = 1
- size = start_or_finish
- end
-
- for i=start, size do
- output[i] = item
- end
-
- return output
- end,
-
- -- Remove all elements from table that @callback returns thruthy for
- -- and returns a new table with the removed items
- -- @obj {table}
- -- @callback {function}
- -- @returns {@table}
- remove = function(obj, callback)
- local output = {}
- local copy = array.deep_copy(obj)
-
- for i=1, #copy do
- local value = copy[i]
-
- if callback(value, i) then
- table.insert(output, value)
- local index = array.index_of(obj, value)
- table.remove(obj, index)
- end
- end
-
- return output
- end
-}
-
-return array
diff --git a/src/array/init.lua b/src/array/init.lua
new file mode 100644
index 0000000..210c5bd
--- /dev/null
+++ b/src/array/init.lua
@@ -0,0 +1,672 @@
+local utils = require('array.utils')
+
+local array
+
+array = {
+ __VERSION = '1.3.6',
+ __DESCRIPTION = "A small library with useful methods to handle Lua's table when it's working like an Array",
+ __LICENSE = [[
+ The MIT License (MIT)
+ Copyright (c) 2017 Evandro Leopoldino Gonçalves
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ ]],
+
+ -- Verify if table object works as an array
+ -- @param obj {table}
+ -- @return {boolean}
+ is_array = function(obj)
+ if not utils.is_table(obj) then return false end
+
+ local i = 0
+ for _ in pairs(obj) do
+ i = i + 1
+ if obj[i] == nil then return false end
+ end
+
+ return true
+ end,
+
+ -- Checks if parameter is an empty table
+ -- @param obj {table}
+ -- @return {boolean}
+ is_empty = function(obj)
+ return array.is_array(obj) and #obj == 0
+ end,
+
+ -- Returns a shallow copy of a portion of a table into a new table
+ -- @param obj {table}
+ -- @param start {number} start value
+ -- @param finish {number} end value
+ -- @return {boolean}
+ slice = function(obj, start, finish)
+ utils.raises_error(array, obj, 'slice')
+
+ if array.is_empty(obj) or start == finish then return {} end
+
+ local output = {}
+ local _finish = #obj
+ local _start = 1
+
+ if start >= 0 then
+ _start = start
+ elseif utils.is_nil(finish) and start < 0 then
+ _start = #obj + start + 1
+ end
+
+ if (finish and finish >= 0) then
+ _finish = finish - 1
+ elseif finish and finish < 0 then
+ _finish = #obj + finish
+ end
+
+ for i = _start, _finish do
+ table.insert(output, obj[i])
+ end
+
+ return output
+ end,
+
+ -- Returns the index at which value can be found or -1 in case value is not present
+ -- @param obj {table}
+ -- @param value {*}
+ -- @return {number}
+ index_of = function(obj, value)
+ utils.raises_error(array, obj, 'index_of')
+
+ for i=1, #obj do
+ if obj[i] == value then
+ return i
+ end
+ end
+
+ return -1
+ end,
+
+ -- Return true whether table includes the values, otherwise it returns false
+ -- @param obj {table}
+ -- @param value {*}
+ -- @return {boolean}
+ includes = function(obj, value)
+ utils.raises_error(array, obj, 'includes')
+
+ local index = array.index_of(obj, value)
+
+ if index == -1 then return false end
+ return true
+ end,
+
+ -- Create a new table with reverse values
+ -- @param obj {table}
+ -- @return {table}
+ reverse = function(obj)
+ utils.raises_error(array, obj, 'reverse')
+
+ local output = {}
+
+ for i=#obj, 1, -1 do
+ table.insert(output, obj[i])
+ end
+
+ return output
+ end,
+
+ -- Return first element from the table
+ -- @param obj {table}
+ -- @return {*}
+ first = function(obj)
+ utils.raises_error(array, obj, 'first')
+ return obj[1]
+ end,
+
+ -- Return last element from the table
+ -- @param obj {table}
+ -- @return {*}
+ last = function(obj)
+ utils.raises_error(array, obj, 'last')
+ return obj[#obj]
+ end,
+
+ -- Return maximum value from the table
+ -- @param obj {table}
+ -- @return {*}
+ max = function(obj)
+ utils.raises_error(array, obj, 'max')
+
+ local max = obj[1]
+
+ for i=2, #obj do
+ if obj[i] > max then
+ max = obj[i]
+ end
+ end
+
+ return max
+ end,
+
+ -- Return minimum value from the table
+ -- @param obj {table}
+ -- @return {*}
+ min = function(obj)
+ utils.raises_error(array, obj, 'min')
+
+ local min = obj[1]
+
+ for i=2, #obj do
+ if obj[i] < min then
+ min = obj[i]
+ end
+ end
+
+ return min
+ end,
+
+ -- Create a new table of values by mapping each value in table through a transformation function
+ -- @param obj {table}
+ -- @param callback {function}
+ -- @return {*}
+ map = function(obj, callback)
+ utils.raises_error(array, obj, 'map')
+
+ local initial_value = {}
+ local reducer = function(accumulator, current, i)
+ table.insert(accumulator, callback(current, i))
+ return accumulator
+ end
+
+ return array.reduce(obj, reducer, initial_value)
+ end,
+
+ -- Create a new table containing all elements that pass truth test
+ -- @param obj {table}
+ -- @param callback {function}
+ -- @return {*}
+ filter = function(obj, callback)
+ utils.raises_error(array, obj, 'filter')
+
+ local initial_value = {}
+ local reducer = function(accumulator, current)
+ if callback(current) then
+ table.insert(accumulator, current)
+ end
+
+ return accumulator
+ end
+
+ return array.reduce(obj, reducer, initial_value)
+ end,
+
+ -- Applies a function against an accumulator and each value of the table to reduce it to a single value
+ -- @param obj {table}
+ -- @param callback {function}
+ -- @param memo {table}
+ -- @return {*}
+ reduce = function(obj, callback, memo)
+ utils.raises_error(array, obj, 'reduce')
+
+ local initialIndex = 1
+ local _memo = memo
+
+ if _memo == nil then
+ initialIndex = 2
+ _memo = obj[1]
+ end
+
+ for i=initialIndex, #obj do
+ _memo = callback(_memo, obj[i], i)
+ end
+
+ return _memo
+ end,
+
+ -- This function is like reduce except that it interates over table's elements from right to left
+ -- @param obj {table}
+ -- @param callback {function}
+ -- @param memo {table}
+ -- @return {*}
+ reduce_right = function(obj, callback, memo)
+ utils.raises_error(array, obj, 'reduce_right')
+
+ local initialIndex = #obj
+ local _memo = memo
+
+ if _memo == nil then
+ initialIndex = initialIndex - 1
+ _memo = obj[#obj]
+ end
+
+ for i=initialIndex, 1, -1 do
+ _memo = callback(_memo, obj[i], i)
+ end
+
+ return _memo
+ end,
+
+ -- Return the sum of the values in table
+ -- @param obj {table}
+ -- @param callback {function}
+ -- @return {number}
+ sum = function(obj)
+ utils.raises_error(array, obj, 'sum')
+
+ return array.reduce(obj, function(memo, value)
+ return memo + value
+ end)
+ end,
+
+ -- Return a new table joining all values from N tables passed by parameter
+ -- @param arg {...}
+ -- @return {table}
+ concat = function(...)
+ local arg = {...}
+ local output = {}
+
+ for i, list in ipairs(arg) do
+ for j=1, #list do
+ table.insert(output, list[j])
+ end
+ end
+
+ return output
+ end,
+
+ -- Create a new table, removing duplicates values
+ -- @param obj {table}
+ -- @return {table}
+ uniq = function(obj)
+ utils.raises_error(array, obj, 'uniq')
+
+ local output = {}
+ local seen = {}
+
+ for i=1, #obj do
+ local value = obj[i]
+ if not seen[value] then
+ seen[value] = true
+ table.insert(output, value)
+ end
+ end
+
+ return output
+ end,
+
+ -- Return a copy of the table with all instances of the values removed
+ -- @param obj {table}
+ -- @param values {table}
+ -- @return {table}
+ without = function(obj, values)
+ utils.raises_error(array, obj, 'without')
+
+ local initial_value = {}
+ local reducer = function(accumulator, current)
+ if (array.includes(values, current) == false) then
+ table.insert(accumulator, current)
+ end
+
+ return accumulator
+ end
+
+ return array.reduce(obj, reducer, initial_value)
+ end,
+
+ -- Tests if at least one element in the table passes the test implemented by the callback
+ -- @param obj {table}
+ -- @param callback {function}
+ -- @return {boolean}
+ some = function(obj, callback)
+ utils.raises_error(array, obj, 'some')
+
+ for i=1, #obj do
+ if callback(obj[i], i) then
+ return true
+ end
+ end
+
+ return false
+ end,
+
+ -- Return a table of the two supplied by pairing up equally-positioned elements from both tables
+ -- @param obj {table}
+ -- @param obj2 {table}
+ -- @return {table}
+ zip = function(obj1, obj2)
+ utils.raises_error(array, obj1, 'zip')
+ utils.raises_error(array, obj2, 'zip')
+
+ local output = {}
+ local size = #obj1 > #obj2 and #obj2 or #obj1
+
+ for i=1, size do
+ table.insert(output, { obj1[i], obj2[i] })
+ end
+
+ return output
+ end,
+
+ -- Run a predicate on each value. If the predicate evaluates to any false value, this function will immediately return false; otherwise, it returns true.
+ -- @param obj {table}
+ -- @param callback {function}
+ -- @return {boolean}
+ every = function(obj, callback)
+ utils.raises_error(array, obj, 'every')
+
+ for i=1, #obj do
+ if not callback(obj[i], i) then
+ return false
+ end
+ end
+
+ return true
+ end,
+
+ -- return a shallow copy of the table passed as parameter
+ -- @param obj {table}
+ -- @return {table}
+ shallow_copy = function(obj)
+ utils.raises_error(array, obj, 'shallow_copy')
+
+ local output = {}
+
+ for i=1, #obj do
+ table.insert(output, obj[i])
+ end
+
+ return output
+ end,
+
+ -- Return a deep copy of the table passed as parameter
+ -- @param value {*}
+ -- @return {table}
+ deep_copy = function(value)
+ local output = value
+
+ if utils.is_table(value) then
+ output = {}
+
+ for i=1, #value do
+ table.insert(output, array.deep_copy(value[i]))
+ end
+ end
+
+ return output
+ end,
+
+ -- Return a new table with the items which exist only in the first table
+ -- @param obj {table}
+ -- @param obj2 {table}
+ -- @return {table}
+ diff = function(obj1, obj2)
+ utils.raises_error(array, obj1, 'diff')
+ utils.raises_error(array, obj2, 'diff')
+
+ local output = {}
+ local hash = utils.convert_to_hash(obj2)
+
+ for i=1, #obj1 do
+ local value = obj1[i]
+
+ if not hash[value] then
+ table.insert(output, value)
+ end
+ end
+
+ return output
+ end,
+
+ -- Create a new table with the sub-table elements concatenated into it up to the specific depth
+ -- @param obj {table, depth}
+ -- @return {table}
+ flat = function(obj, depth)
+ if depth == nil then
+ depth = 1 / 0 -- Infinity
+ end
+
+ return array.reduce(obj, function(acc, item)
+ if array.is_array(item) and depth > 0 then
+ return array.concat(
+ acc,
+ array.flat(item, depth - 1)
+ )
+ else
+ table.insert(acc, item)
+ return acc
+ end
+ end, {});
+ end,
+
+ -- Creates a table filling all the elements from a start index (default one) to an end index with a default value
+ -- @param value {*}
+ -- @param start_or_finish {number}
+ -- @param finish {number}
+ -- @return {table}
+ fill = function(value, start_or_finish, finish)
+ local output = {}
+ local item = value
+ local start = start_or_finish
+ local size = finish
+
+ if finish == nil then
+ start = 1
+ size = start_or_finish
+ end
+
+ for i=start, size do
+ output[i] = item
+ end
+
+ return output
+ end,
+
+ -- Remove all elements from table that @callback return thruthy for
+ -- and return a new table with the removed items
+ -- @param obj {table}
+ -- @param callback {function}
+ -- @return {table}
+ remove = function(obj, callback)
+ local output = {}
+ local copy = array.deep_copy(obj)
+
+ for i=1, #copy do
+ local value = copy[i]
+
+ if callback(value, i) then
+ table.insert(output, value)
+ local index = array.index_of(obj, value)
+ table.remove(obj, index)
+ end
+ end
+
+ return output
+ end,
+
+ -- Return a new table in hash structure, where keys represent each array value
+ -- and value represent the number of times the same item was found in the list
+ -- @param obj {table}
+ -- @return {table}
+ counter = function(obj)
+ local output = {}
+
+ for i=1, #obj do
+ local value = obj[i]
+ output[value] = (output[value] and output[value] or 0) + 1
+ end
+
+ return output
+ end,
+
+ -- Return a new table with the values that exist in both tables
+ -- @param obj1 {table}
+ -- @param obj2 {table}
+ -- @return {table}
+ intersect = function(obj1, obj2)
+ local output = {}
+ local count1 = array.counter(obj1)
+ local count2 = array.counter(obj2)
+
+ for k, v in pairs(count1) do
+ if count2[k] then
+ local new_array = array.fill(
+ k,
+ utils.lowest_value(v, count2[k])
+ )
+
+ utils.multiple_inserts(output, new_array)
+ end
+ end
+
+ return output
+ end,
+
+ -- Return a table composed from key-value pairs
+ -- @param obj1 {table}
+ -- @param obj2 {table}
+ -- @return {table}
+ from_pairs = function(obj)
+ utils.raises_error(array, obj, 'from_pairs')
+
+ local output = {}
+
+ for i=1, #obj do
+ local item = obj[i]
+ output[item[1]] = item[2]
+ end
+
+ return output
+ end,
+
+ -- Executes callback once for each table element
+ -- @param obj {table}
+ -- @param callback {function}
+ -- @return {void}
+ each = function(obj, callback)
+ for i=1, #obj do
+ callback(obj[i], i)
+ end
+ end,
+
+ -- Executes callback once for each table element in reverse order
+ -- @param obj {table}
+ -- @param callback {function}
+ -- @return {void}
+ reverse_each = function(obj, callback)
+ for i = #obj, 1, -1 do
+ callback(obj[i], i)
+ end
+ end,
+
+ -- Returns a new table composed by keys created from the results of running each element through `callback`
+ -- @param obj {table}
+ -- @param callback {function}
+ -- @return {table}
+ group_by = function(obj, callback)
+ utils.raises_error(array, obj, 'group_by')
+
+ function reducer(accumulator, current)
+ local result = callback(current)
+ if not accumulator[result] then
+ accumulator[result] = {}
+ end
+
+ table.insert(accumulator[result], current)
+
+ return accumulator
+ end
+
+ return array.reduce(obj, reducer, {})
+ end,
+
+ -- Returns a value from a random key of the given array
+ -- @param obj {table}
+ -- @return {*}
+ random = function(obj)
+ utils.raises_error(array, obj, 'random')
+ return obj[math.random(#obj)]
+ end,
+
+ -- Creates a new table returning all permutations of length of the elements of the given table
+ -- @param obj {table}
+ -- @return {table}
+ permutation = function(obj)
+ local output = {}
+
+ if #obj == 1 then
+ return { obj }
+ end
+
+ local partial_permutations = array.permutation(array.slice(obj, 2))
+ local first = obj[1]
+
+ for i=1, #partial_permutations do
+ local partial = partial_permutations[i]
+
+ for j=1, #partial+1 do
+ local permutation_front = array.slice(partial, 1, j)
+ local permutation_after = array.slice(partial, j)
+
+ table.insert(output, array.concat(permutation_front, { first }, permutation_after))
+ end
+ end
+
+ return output
+ end,
+
+ -- Returns a new array-like table with elements splitted into groups of length of `size`
+ -- @param obj {table}
+ -- @param size {number}
+ -- @return {table}
+ chunk = function(obj, size)
+ local output = {}
+
+ for _, v in pairs(obj) do
+ local last = output[#output]
+
+ if utils.is_nil(last) or #last == size then
+ table.insert(output, { v })
+ else
+ table.insert(last, v)
+ end
+ end
+
+ return output
+ end,
+
+ -- Returns a new array-like table by applying a given callback to each element of the table, and then flattening the result by one level
+ -- @param obj {table}
+ -- @param callback {function}
+ -- @return {table}
+ flat_map = function(obj, callback)
+ utils.raises_error(array, obj, 'flat_map')
+ local mapped = array.map(obj, callback)
+
+ return array.flat(mapped)
+ end,
+
+ -- Creates a new table composed of keys generated from the results of running each element of the given table through the given callback
+ -- @param obj {table}
+ -- @param callback {function}
+ -- @return {table}
+ key_by = function(obj, callback)
+ utils.raises_error(array, obj, 'key_by')
+
+ return array.reduce(obj, function(accumulator, current)
+ local key = callback(current)
+ accumulator[key] = current
+ return accumulator
+ end, {})
+ end
+}
+
+return array
diff --git a/src/array/utils.lua b/src/array/utils.lua
new file mode 100644
index 0000000..5f1c472
--- /dev/null
+++ b/src/array/utils.lua
@@ -0,0 +1,66 @@
+return {
+ -- Helper function to check if value passed by parameter is a table
+ -- @obj {table}
+ -- @returns {boolean}
+ is_table = function(obj)
+ return type(obj) == 'table'
+ end,
+
+ -- Raises error if @param is not an array
+ -- @obj {table}
+ -- @param {table}
+ -- @method {string}
+ -- @returns {void}
+ raises_error = function(obj, param, method)
+ assert(obj.is_array(param), string.format('%s expects an array', method))
+ end,
+
+ -- Returns lowest value between two values
+ -- @a {number}
+ -- @b {number}
+ -- @returns number
+ lowest_value = function(a, b)
+ return a < b and a or b
+ end,
+
+ -- Makes multiple inserts in a table (array-like)
+ -- @obj {table}
+ -- @values {table}
+ -- @returns {void}
+ multiple_inserts = function(obj, values)
+ for i=1, #values do
+ local value = values[i]
+ table.insert(obj, value)
+ end
+ end,
+
+ -- Convert array to hash table
+ -- @obj {table}
+ -- @returns {table}
+ convert_to_hash = function(obj)
+ local output = {}
+
+ for i=1, #obj do
+ local value = obj[i]
+ output[value] = true
+ end
+
+ return output
+ end,
+
+ -- Loop through table and print all values
+ -- @obj {table}
+ -- @returns {nil}
+ print_items = function(obj)
+ for i=1, #obj do
+ print(obj[i])
+ end
+ end,
+
+ -- Checks wether value is nil
+ -- @obj {any}
+ -- @returns {boolean}
+ is_nil = function(value)
+ return value == nil
+ end
+}
diff --git a/tasks/default_rockspec b/tasks/default_rockspec
index 76a68a3..46e57fa 100644
--- a/tasks/default_rockspec
+++ b/tasks/default_rockspec
@@ -20,6 +20,7 @@ dependencies = {
build = {
type = "builtin",
modules = {
- ['array'] = "array.lua"
+ ["array"] = "src/array/init.lua",
+ ["array.utils"] = "src/array/utils.lua"
}
}
diff --git a/test.lua b/test.lua
index 350ef62..e5b5a3f 100644
--- a/test.lua
+++ b/test.lua
@@ -1,355 +1,448 @@
-local array = require 'array'
local test = require 'simple_test'
+local array = require 'array'
test('meta infos', function(a)
- a.equal(array.__VERSION, '1.2.6')
+ a.equal(array.__VERSION, '1.3.6')
a.equal(array.__DESCRIPTION,
"A small library with useful methods to handle Lua's table when it's working like an Array")
end)
-test('is_array should return false when object is not a table', function(a)
- a.equal(array.is_array('lua'), false)
-end)
-
-test('is_array should return false when the table is working like a dictionary', function(a)
- a.equal(array.is_array({ language='lua' }), false)
-end)
-
-test('is_array should return true when table is empty', function(a)
- a.equal(array.is_array({}), true)
-end)
-
-test('is_array should return true when table is working like an array', function(a)
- a.equal(array.is_array({ 'a', 'b', 'c', 'd' }), true)
-end)
-
-test('is_empty should return false when table has at least one item', function(a)
- a.equal(array.is_empty({ 'a' }), false)
+test('is_array', function(a)
+ a.not_ok(array.is_array('lua'))
+ a.not_ok(array.is_array({ language='lua' }))
+ a.ok(array.is_array({}))
+ a.ok(array.is_array({ 'a', 'b', 'c', 'd' }))
end)
-test('is_empty should return false when table does not have any item', function(a)
- a.equal(array.is_empty({}), true)
+test('is_empty', function(a)
+ a.not_ok(array.is_empty({ 'a' }))
+ a.ok(array.is_empty({}))
end)
-test('first should return first item from table', function(a)
+test('first', function(a)
a.equal(array.first({ 'a', 'b', 'c', 'd' }), 'a')
end)
-test('last should return last item from table', function(a)
+test('last', function(a)
a.equal(array.last({ 'a', 'b', 'c', 'd' }), 'd')
end)
-test('slice should return an empty table when it does not have any element', function(a)
+test('slice', function(a)
a.equal(#array.slice({}, 1, 2), 0)
-end)
-test('slice should return a table with values between start index and end index', function(a)
- local result = array.slice({ 'lua', 'javascript', 'python', 'ruby', 'c' }, 2, 4)
+ a.deep_equal(
+ array.slice({ 'lua', 'javascript', 'python', 'ruby', 'c' }, 2, 5),
+ { 'javascript', 'python', 'ruby' }
+ )
- a.equal(type(result), 'table')
- a.equal(#result, 3)
- a.equal(result[1], 'javascript')
- a.equal(result[2], 'python')
- a.equal(result[3], 'ruby')
-end)
+ a.deep_equal(
+ array.slice({ 'lua', 'javascript', 'python', 'ruby', 'c' }, 2),
+ { 'javascript', 'python', 'ruby', 'c' }
+ )
-test('slice should return a table with every values from start index until last index', function(a)
- local result = array.slice({ 'lua', 'javascript', 'python', 'ruby', 'c' }, 2)
+ a.deep_equal(
+ array.slice({ 'lua', 'javascript', 'python', 'ruby', 'c' }, -2),
+ { 'ruby', 'c' }
+ )
- a.equal(type(result), 'table')
- a.equal(#result, 4)
- a.equal(result[1], 'javascript')
- a.equal(result[2], 'python')
- a.equal(result[3], 'ruby')
- a.equal(result[4], 'c')
+ a.deep_equal(
+ array.slice({ 'lua', 'javascript', 'python', 'ruby', 'c' }, 2, -2),
+ { 'javascript', 'python' }
+ )
end)
-test('reverse should return an inverted table', function(a)
- local result = array.reverse({ 'lua', 'javascript', 'python' })
-
- a.equal(type(result), 'table')
- a.equal(#result, 3)
- a.equal(result[1], 'python')
- a.equal(result[2], 'javascript')
- a.equal(result[3], 'lua')
+test('reverse', function(a)
+ a.deep_equal(
+ array.reverse({ 'lua', 'javascript', 'python' }),
+ { 'python', 'javascript', 'lua' }
+ )
end)
-test('map should return a table with 2, 4, 6 values', function(a)
- local result = array.map({ 1, 2, 3 }, function(value)
- return value * 2
- end)
-
- a.equal(type(result), 'table')
- a.equal(#result, 3)
- a.equal(result[1], 2)
- a.equal(result[2], 4)
- a.equal(result[3], 6)
+test('map', function(a)
+ a.deep_equal(
+ array.map({ 1, 2, 3 }, function(value)
+ return value * 2
+ end),
+ { 2, 4, 6 }
+ )
end)
-test('filter should return a table with 10, 15, 20 values', function(a)
- local result = array.filter({ 15, 10, 5, 3, 20 }, function(value)
- return value >= 10
- end)
-
- a.equal(type(result), 'table')
- a.equal(#result, 3)
- a.equal(result[1], 15)
- a.equal(result[2], 10)
- a.equal(result[3], 20)
+test('filter', function(a)
+ a.deep_equal(
+ array.filter({ 15, 10, 5, 3, 20 }, function(value)
+ return value >= 10
+ end),
+ { 15, 10, 20 }
+ )
end)
-test('max should return the biggest value from a table', function(a)
+test('max', function(a)
a.equal(array.max({ 20, 22, 1, 3, 30, 42 }), 42)
-end)
-
-test('max should return nil when table is empty', function(a)
a.equal(array.max({}), nil)
end)
-test('min should return the smallest value from a table', function(a)
+test('min', function(a)
a.equal(array.min({ 20, 22, 1, 3, 30, 42 }), 1)
-end)
-
-test('min should return nil when table is empty', function(a)
a.equal(array.min({}), nil)
end)
-test('reduce should return 90', function(a)
- local result = array.reduce({ 20, 30, 40 }, function(memo, value)
- return memo + value
- end)
+test('reduce', function(a)
+ a.equal(
+ array.reduce({ 20, 30, 40 }, function(memo, value)
+ return memo + value
+ end),
+ 90
+ )
- a.equal(result, 90)
+ a.equal(
+ array.reduce({ 'a', 'b', 'c', 'd', 'e' }, function(memo, value)
+ return memo .. value
+ end),
+ 'abcde'
+ )
end)
-test('reduce should return 100', function(a)
- local result = array.reduce({ 20, 30, 40 }, function(memo, value)
- return memo + value
- end, 10)
+test('reduce_right', function(a)
+ a.equal(
+ array.reduce_right({ 20, 30, 40 }, function(memo, value)
+ return memo + value
+ end),
+ 90
+ )
- a.equal(result, 100)
+ a.equal(
+ array.reduce_right({ 'a', 'b', 'c', 'd', 'e' }, function(memo, value)
+ return memo .. value
+ end),
+ 'edcba'
+ )
end)
-test('reduce should return first item', function(a)
- local result = array.reduce({ 20 }, function(memo, value)
- return memo + value
- end)
-
- a.equal(result, 20)
+test('sum', function(a)
+ a.equal(
+ array.sum({10, 20, 30, 40, 50}),
+ 150
+ )
end)
-test('reduce should concatenate all items', function(a)
- local result = array.reduce({ 'a', 'b', 'c', 'd', 'e' }, function(memo, value)
- return memo .. value
- end)
+test('index_of', function(a)
+ a.equal(
+ array.index_of({ 20, 30, 40, 50 }, 40),
+ 3
+ )
- a.equal(result, 'abcde')
+ a.equal(
+ array.index_of({ 20, 30, 40 }, 50),
+ -1
+ )
end)
-test('reduce_right should concatenate all items starting from right to left', function(a)
- local result = array.reduce_right({ 'a', 'b', 'c', 'd', 'e' }, function(memo, value)
- return memo .. value
- end)
-
- a.equal(result, 'edcba')
-end)
+test('concat', function(a)
+ a.deep_equal(
+ array.concat({ 1, 2, 3 }, { 4, 5, 6 }),
+ { 1, 2, 3, 4, 5, 6 }
+ )
-test('reduce_right should return 100', function(a)
- local result = array.reduce_right({ 20, 30, 40 }, function(memo, value)
- return memo + value
- end, 10)
+ a.deep_equal(
+ array.concat({ 'a', 'b', 'c' }, { 'd', 'e' }, { 'f', 'g' }),
+ { 'a', 'b', 'c', 'd', 'e', 'f', 'g' }
+ )
- a.equal(result, 100)
+ a.deep_equal(
+ array.concat({ 1, 2, 3 }, { 4, 5 }, { 6, 7 }, { 8, 9 }),
+ { 1, 2, 3, 4, 5, 6, 7, 8, 9 }
+ )
end)
-test('reduce_right', function(a)
- local result = array.reduce_right({ 'a', 'b', 'c', 'd', 'e' }, function(memo, value)
- return memo .. value
- end)
-
- a.equal(result, 'edcba')
+test('uniq', function(a)
+ a.deep_equal(
+ array.uniq({ 'a', 'b', 'a', 'b', 'c', 'd' }),
+ { 'a', 'b', 'c', 'd' }
+ )
end)
-test('sum should return sum of the values in table', function(a)
- local result = array.sum({10, 20, 30, 40, 50})
- a.equal(result, 150)
+test('without', function(a)
+ a.deep_equal(
+ array.without({ 10, 20, 30, 10, 4 }, { 10, 4 }),
+ { 20, 30 }
+ )
end)
-test('index_of should return correct position of value in the table', function(a)
- a.equal(array.index_of({ 20, 30, 40, 50 }, 40), 3)
-end)
+test('some', function(a)
+ a.ok(
+ array.some({'a', 'b', 'c'}, function(element)
+ if (element == 'b') then return true end
+ end)
+ )
-test('index_of should return -1 when the value is not in the table', function(a)
- a.equal(array.index_of({ 20, 30, 40 }, 50), -1)
+ a.not_ok(
+ array.some({'a', 'b', 'c'}, function(element)
+ if (element == 'd') then return true end
+ end)
+ )
end)
-test('concat should join the two array', function(a)
- local result = array.concat({ 1, 2, 3 }, { 4, 5, 6 })
+test('zip', function(a)
+ a.deep_equal(
+ array.zip({ 'a', 'b' }, { 'A', 'B', 'C' }),
+ {
+ { 'a', 'A' },
+ { 'b', 'B' }
+ }
+ )
- a.equal(#result, 6)
- a.equal(result[4], 4)
+ a.deep_equal(
+ array.zip({ 'a', 'b' }, { 'A', 'B' }),
+ {
+ { 'a', 'A' },
+ { 'b', 'B' }
+ }
+ )
end)
-test('uniq should return every value once', function(a)
- local result = array.uniq({ 'a', 'b', 'a', 'b', 'c', 'd' })
+test('every', function(a)
+ a.not_ok(
+ array.every({ 10, 20, 30 }, function(value)
+ return value > 10
+ end)
+ )
- a.equal(#result, 4)
- a.equal(result[1], 'a')
- a.equal(result[2], 'b')
- a.equal(result[3], 'c')
- a.equal(result[4], 'd')
+ a.ok(
+ array.every({ 10, 20, 30 }, function(value)
+ return value >= 10
+ end)
+ )
end)
-test('without should return all values less the items from second array', function(a)
- local result = array.without({ 10, 20, 30, 10, 4 }, { 10, 4 })
+test('shallow_copy', function(a)
+ local obj = {
+ { 'a' },
+ { 'b' }
+ }
- a.equal(#result, 2)
- a.equal(result[1], 20)
- a.equal(result[2], 30)
+ a.deep_equal(
+ array.shallow_copy(obj),
+ obj
+ )
end)
-test('some should return true when exist at least one match', function(a)
- local result = array.some({'a', 'b', 'c'}, function(element)
- if (element == 'b') then return true end
- end)
+test('deep_copy', function(a)
+ local obj = {
+ { 'a' },
+ { 'b' }
+ }
- a.ok(result)
-end)
+ local result = array.deep_copy(obj)
-test('some should return true when exist at least one match', function(a)
- local result = array.some({'a', 'b', 'c'}, function(element)
- if (element == 'd') then return true end
- end)
+ a.equal(#result, #obj)
+ a.equal(result[1][1], obj[1][1])
+ a.equal(result[2][1], obj[2][1])
+
+ obj[1][1] = 'c'
+ a.equal(result[1][1], 'a')
- a.not_ok(result)
+ obj[1] = 'c'
+ a.equal(type(result[1]), 'table')
end)
-test('zip should return a new table with the correct pairs', function(a)
- local result = array.zip({ 'a', 'b' }, { 'A', 'B' })
+test('diff', function(a)
+ local result = array.diff(
+ { 'a', 'b', 'g', 'z', 'h' },
+ { 'a', 'c', 'd', 'e', 'f', 'h', 'x' }
+ )
- a.equal(result[1][1], 'a')
- a.equal(result[1][2], 'A')
- a.equal(result[2][1], 'b')
- a.equal(result[2][2], 'B')
+ a.deep_equal(result, { 'b', 'g', 'z' })
end)
-test('zip should not consider values without pairs', function(a)
- local result = array.zip({ 'a', 'b' }, { 'A', 'B', 'C' })
+test('flat', function(a)
+ a.deep_equal(
+ array.flat({ 'a', 'b', 'c', { 'd', 'e', 'f', { 'g', 'h' } }, { 'i' }, 'j' }),
+ { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' }
+ )
- a.equal(#result, 2)
- a.equal(#result[1], 2)
- a.equal(#result[2], 2)
- a.equal(result[1][1], 'a')
- a.equal(result[1][2], 'A')
- a.equal(result[2][1], 'b')
- a.equal(result[2][2], 'B')
+ a.deep_equal(
+ array.flat({ 'a', 'b', 'c', { 'd', 'e', 'f', { 'g', 'h' } }, { 'i' }, 'j' }, 1),
+ { 'a', 'b', 'c', 'd', 'e', 'f', { 'g', 'h' }, 'i', 'j' }
+ )
+
+ a.deep_equal(
+ array.flat({ 'a', 'b', 'c', { 'd', 'e', { 'f', { 'g', 'h' } } }, { 'i' }, 'j' }, 2),
+ { 'a', 'b', 'c', 'd', 'e', 'f', { 'g', 'h' }, 'i', 'j' }
+ )
end)
-test('every should return true when all elements in the table pass the callback test', function(a)
- local result = array.every({ 10, 20, 30 }, function(value)
- return value >= 10
- end)
+test('fill', function(a)
+ local value = 'Lua'
+ local result = array.fill(value, 3, 4)
+ a.equal(result[3], value)
+ a.equal(result[4], value)
- a.ok(result)
+ a.deep_equal(
+ array.fill(value, 3),
+ { value, value, value }
+ )
end)
-test('every should return false when at least a match fails', function(a)
- local result = array.every({ 10, 20, 30 }, function(value)
- return value > 10
+test('remove', function(a)
+ local list = { 1, 2, 3, 4 }
+ local result = array.remove(list, function(value)
+ return math.fmod(value, 2) == 0
end)
- a.not_ok(result)
-end)
+ a.deep_equal(
+ list,
+ { 1, 3 }
+ )
-test('shallow_copy should return a shallow copy of the array passed as parameter', function(a)
- local obj = {
- { 'a' },
- { 'b' }
- }
+ a.deep_equal(
+ result,
+ { 2, 4 }
+ )
+end)
- local result = array.shallow_copy(obj)
+test('counter', function(a)
+ a.deep_equal(
+ array.counter({ 'a', 'b', 'a', 'a', 'c', 'b' }),
+ { a = 3, b = 2, c = 1 }
+ )
+end)
- a.equal(#result, #obj)
- a.equal(result[1][1], obj[1][1])
- a.equal(result[2][1], obj[2][1])
+test('intersect', function(a)
+ local first_list = { 'a', 'b', 'a', 'c', 'e', 'f', 'a' }
+ local second_list = { 'b', 'a', 'd', 'a' }
- obj[1][1] = 'c'
- a.equal(result[1][1], obj[1][1])
+ a.equal(
+ #array.intersect(first_list, second_list),
+ 3
+ )
+end)
- obj[1] = 'c'
- a.equal(type(result[1]), 'table')
+test('from_pairs', function(a)
+ a.deep_equal(
+ array.from_pairs({ {'a', 1}, {'b', 2} }),
+ { a = 1, b = 2 }
+ )
end)
-test('deep_copy should return a deep copy of the array passed as parameter', function(a)
- local obj = {
- { 'a' },
- { 'b' }
- }
+test('includes', function(a)
+ local list = { 'a', 'b', 'c' }
- local result = array.deep_copy(obj)
+ a.ok(
+ array.includes(list, 'c')
+ )
- a.equal(#result, #obj)
- a.equal(result[1][1], obj[1][1])
- a.equal(result[2][1], obj[2][1])
+ a.not_ok(
+ array.includes(list, 'd')
+ )
+end)
- obj[1][1] = 'c'
- a.equal(result[1][1], 'a')
+test('each', function(a)
+ local list = { 'a', 'b', 'c' }
+ local values = {}
+ local keys = {}
- obj[1] = 'c'
- a.equal(type(result[1]), 'table')
+ array.each(list, function(v, k)
+ table.insert(values, v)
+ table.insert(keys, k)
+ end)
+
+ a.deep_equal(list, values)
+ a.deep_equal(keys, { 1, 2, 3 })
end)
-test('diff should return a new table with the items which exist only in first table', function(a)
- local result = array.diff(
- { 'a', 'b' },
- { 'a', 'c', 'd' }
- )
+test('reverse_each', function(a)
+ local values = {}
+ local keys = {}
- a.equal(#result, 1)
- a.equal(result[1], 'b')
+ array.reverse_each({ 'a', 'b', 'c' }, function(v, k)
+ table.insert(values, v)
+ table.insert(keys, k)
+ end)
+
+ a.deep_equal(values, { 'c', 'b', 'a' })
+ a.deep_equal(keys, { 3, 2, 1 })
end)
-test('flat should return a new table that is an one-dimensional flatting of the table passed by parameter', function(a)
- local obj = { 'a', 'b', 'c', { 'd', 'e', 'f', { 'g', 'h' } }, { 'i' }, 'j' }
- local result = array.flat(obj)
+test('group_by', function(a)
+ a.deep_equal(
+ array.group_by({ 6.1, 4.1, 6.3, 4.4, 5.1 }, function(item) return math.floor(item) end),
+ {
+ [4] = { 4.1, 4.4 },
+ [6] = { 6.1, 6.3 },
+ [5] = { 5.1 }
+ }
+ )
- a.equal(#result, 10)
+ a.deep_equal(
+ array.group_by({ 'one', 'two', 'three', 'four' }, function(item) return #item end),
+ {
+ [3] = { 'one', 'two' },
+ [5] = { 'three' },
+ [4] = { 'four' }
+ }
+ )
end)
-test('fill should create a table with the size passed by parameter and fill every item using the value passed as argument', function(a)
- local value = 1
- local size = 3
- local result = array.fill(value, size)
+test('random', function(a)
+ local list = { 'a', 'b', 'c' }
- a.equal(#result, size)
- a.equal(result[1], value)
- a.equal(result[2], value)
- a.equal(result[3], value)
+ a.ok(
+ array.includes(list, array.random(list))
+ )
+
+ a.equal(array.random({ 'a' }), 'a')
end)
-test('fill should create a table using the value passed by argument from start to end', function(a)
- local value = 'a'
- local start = 3
- local finish = 4
- local result = array.fill(value, start, finish)
+test('permutation', function(a)
+ a.deep_equal(
+ array.permutation({ 'lua', 'javascript' }),
+ {
+ { 'lua', 'javascript' },
+ { 'javascript', 'lua' },
+ }
+ )
- a.equal(result[1], nil)
- a.equal(result[2], nil)
- a.equal(result[3], value)
- a.equal(result[4], value)
+ a.deep_equal(
+ array.permutation({ 'lua', 'javascript', 'ruby' }),
+ {
+ { 'lua', 'javascript', 'ruby' },
+ { 'javascript', 'lua', 'ruby' },
+ { 'javascript', 'ruby', 'lua' },
+ { 'lua', 'ruby', 'javascript' },
+ { 'ruby', 'lua', 'javascript' },
+ { 'ruby', 'javascript', 'lua' },
+ }
+ )
end)
-test('remove should delete items that callback returns thruthy and should return a new table with the removed items', function(a)
- local list = { 1, 2, 3, 4 }
- local result = array.remove(list, function(value)
- return math.fmod(value, 2) == 0
- end)
+test('chunk', function(a)
+ a.deep_equal(
+ array.chunk({ 'a', 'b', 'c', 'd', 'e', 'f', 'g' }, 3),
+ {
+ { 'a', 'b', 'c' },
+ { 'd', 'e', 'f' },
+ { 'g' },
+ }
+ )
+end)
- a.equal(#list, 2)
- a.equal(list[1], 1)
- a.equal(list[2], 3)
+test('flat_map', function(a)
+ a.deep_equal(
+ array.flat_map({ 1, 2, 3 }, function(value)
+ return { value, value * 2 }
+ end),
+ { 1, 2, 2, 4, 3, 6 }
+ )
+end)
- a.equal(#result, 2)
- a.equal(result[1], 2)
- a.equal(result[2], 4)
+test('key_by', function(a)
+ a.deep_equal(
+ array.key_by({ { id = 1, name = 'lua' }, { id = 2, name = 'javascript' } }, function(item)
+ return item.id
+ end),
+ {
+ [1] = { id = 1, name = 'lua' },
+ [2] = { id = 2, name = 'javascript' }
+ }
+ )
end)