A utility library for Lua

introduction

shim.lua is a utility library for lua, which deeply inspired by Underscore.js, Lodash.js, Moses, but more suitable as a lua lib

see at Github repository

Install

wget --no-check-certificate https://raw.githubusercontent.com/chunpu/Shim/master/shim.lua

Usage

local _ = require 'shim'

util

isArray

_.isArray(value)

Check if value is array

_.isArray({1, 2, 3})
-- → true

_.isArray({1, 2, 3, w = 4})
-- → false

isEqual

_.isEqual(a, b)

Compare a and b if they are

_.isEqual(1, 1)
-- → true

_.isEqual({a = 1}, {a = 1})
-- → true

_.isEqual({}, {})
-- → true

it won't compare metatable

trim

_.trim(string, [where])

Remove whitespace at ends of a string, default is both ends

_.trim('  abc   ')
-- → 'abc'

_.trim('    ')
-- → ''

_.trim('  abc  ', 'right')
-- → '  abc'

_.trim('  abc  ', 'left')
-- → 'abc  '

extend

_.extend(dst, ...)

Extend the properties of ... to the destination object, and return the destination object, ... object properties will overwrite dst object properties

_.extend({a = 1}, {b = 2})
-- → {a = 1, b = 2}

_.extend({a = 1, b = 2}, {b = 3})
-- → {a = 1, b = 3}

_.extend({a = 1}, {b = 2}, {c = 3}
-- → {a = 1, b = 2, c = 3}

split

_.split(str, sep)

Split a string into array by separating it with the separator

_.split('q,w,e,r', ',')
-- → {'q', 'w', 'e', 'r'}

_.split('qwer as', ''
-- → {'q', 'w', 'e', 'r', ' ', 'a', 's'}

empty

Check if the value is empty, only when string ~= '' or not empty table will return true. read the demo below before you use the api

_.empty(value)

_.empty(false)
_.empty(true)
_.empty({})
_.empty(0)
_.empty(1)
_.empty('')
_.empty(print)
-- → true

_.empty('0')
_.empty('11111')
_.empty({0})
_.empty({1, 2})
_.empty({a = 1})
-- → false

only

_.only(table, keys)

Return whitelisted properties of a table

_.only({
    a = 1,
    b = 2,
    c = 3
}, {'a', 'b'})
-- →
{a = 1, b = 2}

_.only({
    a = 1,
    b = 2,
    c = 3,
    d = 4
}, 'a c d')
-- →
{
    a = 1,
    c = 3,
    d = 4
}

assertEqual

_.assertEqual(actual, expect, [level = 2])

Same like _.isEqual, but will throw an assertion error when _.isEqual return false

_.assertEqual({a = 1}, {a = 2})

program will exit and throw error with output with

file:line: AssertionError: actual == expect

lua: test.lua:19: AssertionError: {
    'a': 1
} == {
    'a': 2
}
stack traceback:
        [C]: in function 'error'
        ./shim.lua:312: in function 'assertEqual'
        test.lua:19: in main chunk
        [C]: in ?

ok

_.ok(...)

Mostly used in multi test cases, also throw assertion error when fail to match

_.ok(true)

_.ok(false) -- → throw error

_.ok({
    1, 1
}, {
    2, 2
})
-- sugar for
_.assertEqual(1, 1)
_.assertEqual(2, 2)

dump

_.dump(value)

Pretty print a table or other types

print(_.dump({
    a = 1,
    b = {
        a = 1,
        b = {2, 3, 4}
    }
}))

-- →
{
    'b': {
        'b': [2, 3, 4],
        'a': 1
    },
    'a': 1
}

other types like function thread cannot tostring will just print as [type]

print(_.dump({
    thread = coroutine.create(function() end),
    object = coroutine
}))
-- →
{
    'thread': [thread],
    'object': {
        'create': [function],
        'running': [function],
        'status': [function],
        'wrap': [function],
        'yield': [function],
        'resume': [function]
    }
}

array

Most array functions can be used on string too

each

_.each(array, func)

Iterates the array table, executing the callback for each element

callback will execute with three arguments: (value, index, array)

_.each({11, 22, 33}, print)
-- →
11      1       table: 0x1fa7600
22      2       table: 0x1fa7600
33      3       table: 0x1fa7600

_.each can only iterate array table or string, it return the first argument and don't do anything if not the right type

so you don't have to check the type before use the api like

-- not necessary, _.each will check it for you
if type(array) == 'table' then
    _.each(array, print)
end

_.each is the base function for other api like _.map, _.filter, so you can also use them directly without type checking

_each

_._each(array, func)

Like _.each above, but break loop when func return false

local arr = {}
_._each({1, 2, 3, 4}, function(x)
    if x > 2 then return false end -- break loop
    table.insert(arr, x)
end)
arr -- → {1, 2}

every

_.every(array, func)

Check if all the callback return values are truthy

_.every({1, 2, 3}, function(x)
    return x > 0
end)
-- → true

_.every({1, 2, 3}, function(x)
    return x > 2
end)
-- → false

some

_.some(array, func)

Check if any callback return value is truthy

_.some({1, 2, 3}, function(x)
    return x > 2
end)
-- → true

_.some({1, 2, 3}, function(x)
    return x > 4
end)
-- → false

map

_.map(array, func)

Return a new array of the results of each callback execution

_.map({1, 0, 2, 4}, function(x)
    return x + 1
end)
-- → {2, 1, 3, 5}

has

_.has(array|string, item)

Return true if item in the array|string

_.has('qwert', 'rt')
-- → true

_.has({1, 2, 3, 4}, 3)
-- → true

sub

_.sub(string, start, end)

Return substring of a string by start and end index

_.sub('qwer', 2, 3)
-- → 'we'

flatten

_.flatten(array)

Flatten a nested array

_.flatten({1, {2, {3, {4}}}})
-- → {1, 2, 3, 4}

push

_.push(array, ...)

Push ... to the end of the array

_.push({1, 2, 3}, 4, 5)
-- → {1, 2, 3, 4, 5}

uniq

_.uniq(array)

Remove duplicated values in the array

it use table k, v so it won't keep the order

_.uniq({1, 2, 3, 2, 1})
-- → {1, 2, 3}

union

_.union(...)

Create an array of unique values, if nested then flatten first

_.sort(_.union({1, 2, 3}, {5, 2, 1, 4}, {2, 1}))
-- → {1, 2, 3, 4, 5}

sort

_.sort(array, [func])

Alias for table.sort(table, [func])

_.sort({3, 2, 4, 1})
-- → {1, 2, 3, 4}

filter

_.filter(array, func)

Return a new array of elements that passed the callback check

_.filter({1, 2, 3, 4, 5}, function(x)
    return x > 3
end
-- → {4, 5}

indexOf

_.indexOf(array|string, value, [from = 1], [isPlain = false])

Get the index of the matched value or nil

isPlain = true turns off the pattern matching facilities

_.indexOf({11, 22, 33}, 22)
-- → 2

_.indexOf({11, 22, 33}, 44)
-- → nil

_.indexOf({11, 22, 33, 33, 22, 11}, 22, 3)
-- → 5

lastIndexOf

_.lastIndexOf(array|string, value, [from = #array], [isPlain = false])

Match from end of the array, get the index of the matched value or nil

_.lastIndexOf({11, 22, 33, 11}, 11)
-- → 4
_.lastIndexOf({11, 22, 33, 11}, 0)
-- → nil
_.lastIndexOf({11, 22, 33}, 11)
-- → 1

difference

_.difference(array, other)

Create an array excluding all values of the second array

_.difference({1, 2, 3, 4, 5}, {5, 2, 10})
-- → {1, 3, 4}

without

_.without(array, ...)

Create an array excluding all provided arguments

_.without({1, 4, 3, nil, 0, ''}, nil, 0, '')
-- → {1, 4, 3}

reduce

_.reduce(array, fn, [prev])

Reduce a collection to a value which is the accumulated result of running each element in the collection through the callback

_.reduce({1, 2, 3, 4}, function(ret, k)
    return ret + k
end, 0)
-- → 10

invoke

_.invoke(arr, fn)

Just like map now

_.invoke({'1', '2', '3'}, tonumber)
-- → {1, 2, 3}

chaining

_

_(value)

_ is callable, _(value) creates a table wraps the given value

_({1, 0, 2, 4}):sort()
-- → {0, 1, 2, 4}

Tips: a:b(args) is sugar for a.b(self, args), self is a

chain

_(value):chain()

Make the wrapped table chainable, remember to use :value() to get the value you need

_({1, 0, 2, 4})
    :chain()
    :sort()
    :map(function(x) return x * 2 end)
    :filter(function(x) return x < 6 end)
    :value()
-- → {0, 2, 4}