Fork me on GitHub

Iterator Functions

Array Functions

Table Functions

Function Functions

Utility Functions

Chaining Functions

Underscore.lua

Underscore.lua is a Lua library that provides a set of utility functions for dealing with iterators, arrays, tables, and functions. It's api and documentation are heavily inspired by Underscore.js.

Installing

It can be installed with LuaRocks via:

luarocks install underscore.lua 
  --from=http://marcusirven.s3.amazonaws.com/rocks/

Alternatively underscore.lua can be downloaded directly from here.

The source code is available at Github.

Basic Usage

_ = require 'underscore'
_.each({1,2,3}, print)

It is idiomatic in Lua to use the underscore character for throw away variables so you can simply assign it to another variable name such as:

__ = require 'underscore'
__.each({1,2,3}, print)

Iterator Functions and Arrays

All the iterator functions can take either an iterator such as:

function sq(n)
  return coroutine.wrap(function() 
    for i=1,n do
      coroutine.yield(i*i)
    end
  end)
end

_.each(sq(5), print)
=> 1
=> 4
=> 9
=> 16
=> 25

or and array such as:

_.each({1,2,3,4,5}, print)
=> 1
=> 2
=> 3
=> 4
=> 5

This is accomplished with _.iter() which with create an iterator over and array or if passed a function it will use that.

for i in _.iter({1,2,3,4,5}) do
  print(i)
end
=> 1
=> 2
=> 3
=> 4
=> 5

Object-Oriented and Functional Styles

You can use Underscore in either an object-oriented or a functional style, depending on your preference. The following two lines of code are identical ways to double a list of numbers.

_.map({1, 2, 3}, function(n) return n * 2 end)
_({1, 2, 3}):map(function(n) return n * 2 end)

Using the object-oriented style allows you to chain together methods. Calling chain on a wrapped object will cause all future method calls to return wrapped objects as well. When you've finished the computation, use value to retrieve the final value. Here's an example of chaining together a map/flatten/reduce, in order to get the word count of every word in a song.

local lyrics = {
  { line = 1, words = "I'm a lumberjack and I'm okay" },
  { line = 2, words = "I sleep all night and I work all day" },
  { line = 3, words = "He's a lumberjack and he's okay" },
  { line = 4, words = "He sleeps all night and he works all day" }
}

_(lyrics):chain()
  :map(function(line) 
    local words = {}
    for w in line.words:gmatch("%S+") do
      words[#words+1] = w   
    end
    return words
  end)
  :flatten()
  :reduce({}, function(counts, word)
    counts[word] = (counts[word] or 0) + 1
    return counts
  end):value()

Note: This can be written even more succinctly as (notice the map function):

_(lyrics):chain()
  :map(function(line) return _.to_array(line.words:gmatch("%S+")) end)
  :flatten()
  :reduce({}, function(counts, word)
    counts[word] = (counts[word] or 0) + 1
    return counts
  end):value()

Reference

Iterator Functions

map _.map(iter, func) Aliases: collect

Produces a new array by mapping each value in iter through a transformation function.

_.map({1,2,3}, function(i) return i*2 end)
=> { 2,4,6 }

each _.each(iter, func) Aliases: for_each

Passes each value to function(i) and returns the input.

_.each({1,2,3}, print)
=> {1,2,3}

select _.select(iter, func) Aliases: filter

Removes items that do not match the provided criteria.

_.select({1,2,3}, function(i) return i%2 == 1 end)
=> {1,3}

reject _.reject(iter, func)

Removes items that match the provided criteria.

_.reject({1,2,3}, function(i) return i%2 == 1 end)
=> {2}

invoke _.invoke(foo, blah)

Calls a function with specified name on each item using the colon operator.

Person = {}
Person.__index = Person 
function Person:new(name) 
  return setmetatable({ name=name }, self) 
end 
function Person:print() 
  print(self.name) 
end 
_.invoke({ Person:new("Tom"), Person:new("Dick"), Person:new("Harry") }, "print") 
=> Calls person:print() on each Person

pluck _.pluck(iter, property_name)

An convenient version of the common use-case of mapextracting a list of properties.

_.pluck({ {id=1}, {id=2}, {id=3} }, 'id') 
=> { 1, 2, 3 }

reduce _.reduce(iter, memo, func) Aliases: inject,foldl

Boils down a list of values into a single value. Memo is the initial state of the reduction, and each successive step of it should be returned by func.

_.reduce({1,2,3}, 0, function(memo, i) return memo+i end)
=> 6

max _.max(iter, [func])

Returns the item with the largest value. If a func is provided it will be used on each value to generate the criterion by which the value is ranked.

_.max({1,2,3,4}) 
=> 4
_.max({ {age=15}, {age=12}, {age=19} }, function(p) return p.age end) 
=> {age=19}

min _.min(iter, [func])

Returns the item with the smallest value. If a func is provided it will be used on each value to generate the criterion by which the value is ranked.

_.max({1,2,3,4}) 
=> 1
_.max({ {age=15}, {age=12}, {age=19} }, function(p) return p.age end) 
=> {age=12}

include _.include(iter, value)

Returns true if the list include's value. Uses the == operator.

_.include({1,2,3,4}, function(i) return i%2 == 0 end)
=> true
_.include({1,3,5}, function(i) return i%2 == 0 end)
=> false

detect _.detect(iter, func)

Looks through a list returning the first element that matches a truth function. The function returns as soon as it finds an acceptable element.

_.detect({1,2,3,4}, func(i) return i > 3 end)
=> 4
_.detect({1,2,3,4}, func(i) return i > 7 end)
=> nil

all _.all(iter, [func]) Aliases: every

Returns true if func(item) returns true for all item in items.

_.all({2,4,8}, function(i) return i%2 == 0 end)        
=> true
_.all({1,2,3,4}, function(i) return i%2 == 0 end)        
=> false

any _.any(iter, [func]) Aliases: some

Returns true if func(item) returns true for any item in items.

_.any({1,2,3,4}, function(i) return i%2 == 0 end)        
=> true
_.any({1,3,5}, function(i) return i%2 == 0 end)        
=> false

to_array _.to_array(iter)

Collects all values into a new array.

_.to_array(string.gmatch("dog cat goat", "%S+")) 
=> { "dog", "cat", "goat" }

sort _.sort(iter, [comparison_func])

Returns an array with all elements sorted by the comparison function, by default it uses the < operator. If an array is passed in it will sort the elements in place and not create a new array.

_.sort({ 3, 1, 2}) 
=> { 1, 2, 3 }

reverse _.reverse(iter, [comparison_func])

Iterates over all the items and returns a new array with the items in reverse order.

_.reverse({ 1, 2, 3}) 
=> { 3, 2, 1 }

Array Functions

flatten _.flatten(array)

Flattens a nested array (the nesting can be to any depth).

_.flatten({1, {2}, {3, {{{4}}}}}) 
=> { 1, 2, 3, 4 }

first _.first(array, [length]) Aliases: head

Returns the first element of an array. Optionally it will return an array of the first n items.

_.first({1,2,3}) 
=> 1
_.first({1,2,3}, 2) 
=> {1,2,}

rest _.rest(array, [start_index]) Aliases: tail

Returns an array of all items except the first. Optionally it will start at start_index.

_.rest({1,2,3}) 
=> {2,3} 
_.rest({1,2,3}, 2) 
=> {3}

slice _.slice(array, start_index, length)

Returns a section of an array starting with start_index of length items length.

_.slice({ 1, 2, 3, 4, 5 }, 2, 3) 
=> { 2, 3, 4 }

push _.push(array, item)

Inserts item at the end of an array

_.push({1,2,3}, 4)
=> {1,2,3,4}

pop _.pop(array)

Removes and returns the last item in an array

_.pop({1,2,3})
=> 3

shift _.shift(array)

Removes and returns the first item in an array

_.shift({1,2,3})
=> 1

unshift _.unshift(array, item)

Inserts item at the beginning of an array

_.push({1,2,3}, 4)
=> {4,1,2,3}

join _.join(array)

Returns a string with all items in the array concatenated together with an optional separator.

_.join({'c','a','t'})
=> "cat"
_.join({'c','a','t'}, '/')
=> "c/a/t"

Table Functions

extend _.extend(destination, source)

Copy all of the properties in the source object over to the destination object.

_.extend({ name = 'moe' }, { age = 50 }) 
=> { name = 'moe', age = 50 }

keys _.keys(object)

Returns an array of all the property names in a table. (Note: order is not defined)

_.keys { name = "John", age = 25 }
=> { "name", "age" }

values _.values(object)

Returns an array of all the property values in a table. (Note: order is not defined)

_.values { name = "John", age = 25 }
=> { "John", 25 }

is_empty _.is_empty(object)

Returns true if object contains no values.

_.is_empty({}) 
=> true 
-.is_empty({ name = "moe" }) 
=> false

Function Functions

curry _.curry(func, arg)

Creates a wrapper function substituing the supplied arg for the first argument in original function.

function f(p1,p2) return {p1,p2} end
g = _.curry(f, "a")
g("b")
=> {"a","b"}
g("c")
=> {"a","c"}

wrap _.wrap(func, wrapper)

Wraps the first function inside of the wrapper function, passing it as the first argument. This allows the wrapper to execute code before and after the function runs, adjust the arguments, and execute it conditionally.

hello = function(name) 
  return "hello: "..name 
end 
hello = _.wrap(hello, function(func, ...) 
  return "before, "..func(...)..", after" 
end) 
hello('moe') 
=> before, hello: moe, after

compose _.compose(func1, func2, [...])

Returns the composition of a list of functions, where each function consumes the return value of the function that follows. In math terms, composing the functions f(), g(), and h() produces f(g(h())).

greet = function(name) 
  return "hi: "..name 
end 
exclaim = function(statement) 
  return statement.."!" 
end 
welcome = _.compose(print, greet, exclaim) 
welcome('moe') 
=> hi: moe!

Utility Functions

functions _.functions()

Returns a list of function names in this library.

_.functions() 
=> { 'each', 'map', 'reduce', ... }

identity _.identity(v)

Identity function, simply returns whatever is passed in. This function looks useless, but is used within Underscore as a default function.

_.identity("foo") 
=> "foo"

iter _.iter()

Creates an iterator function over an array.

for i in _.iter({1,2,3}) do
  print(i)
end

range _.range(start_or_length, [end], [step])

Iterates over a range of integers

_.range(5,10):to_array()
=> { 5,6,7,8,9,10 }
_.range(10):to_array()
=> { 1,2,3,4,5,6,7,8,9,10 }
_.range(2,10,2):to_array()
=> { 2,4,6,8,10 }
_.range(10,2,-2):to_array()
=> { 10,8,6,4,2 }

is_equal _.is_equal(o1,o2,[ignore_mt])

Performs an optimized deep comparison between the two objects, to determine if they should be considered equal. By default it uses the _eql metamethod if it is present.

_.is_equal({1,2,3},{1,2,3})
=> true
_.is_equal({a=1,b=2},{a=1,b=2})
=> true
_.is_equal({a=1,b=2},{a=2,b=3})
=> false

Chaining Functions

chain _.chain()

Puts underscore into chaining mode.

_.({1,2,3,4}):chain():map(function(i) return i+1 end):select(function(i) i%2 == 0 end):value()
=> { 2,4 }

value _.value()

Stops chaining and returns the current value.

_.({1,2,3,4}):chain():map(function(i) return i+1 end):select(function(i) i%2 == 0 end):value()
=> { 2,4 }