Advertisement
Lemur

underscore.lua

Sep 9th, 2014
326
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 10.61 KB | None | 0 0
  1. -- Copyright (c) 2009 Marcus Irven - http://mirven.github.io/underscore.lua/
  2. -- Slightly cleaned spaces for readability and file size. Cheers!
  3. --  
  4. -- Permission is hereby granted, free of charge, to any person
  5. -- obtaining a copy of this software and associated documentation
  6. -- files (the "Software"), to deal in the Software without
  7. -- restriction, including without limitation the rights to use,
  8. -- copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. -- copies of the Software, and to permit persons to whom the
  10. -- Software is furnished to do so, subject to the following
  11. -- conditions:
  12. --  
  13. -- The above copyright notice and this permission notice shall be
  14. -- included in all copies or substantial portions of the Software.
  15. --  
  16. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. -- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  18. -- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. -- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  20. -- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  21. -- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  22. -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  23. -- OTHER DEALINGS IN THE SOFTWARE.
  24.  
  25. --- Underscore is a set of utility functions for dealing with
  26. -- iterators, arrays, tables, and functions.
  27.  
  28. local Underscore   = { funcs = {} }
  29. Underscore.__index = Underscore
  30.  
  31. function Underscore.__call(_, value)
  32.   return Underscore:new(value)
  33. end
  34.  
  35. function Underscore:new(value, chained)
  36.   return setmetatable({ _val = value, chained = chained or false }, self)
  37. end
  38.  
  39. function Underscore.iter(list_or_iter)
  40.   if type(list_or_iter) == "function" then return list_or_iter end
  41.  
  42.   return coroutine.wrap(function()
  43.     for i=1,#list_or_iter do
  44.       coroutine.yield(list_or_iter[i])
  45.     end
  46.   end)
  47. end
  48.  
  49. function Underscore.range(start_i, end_i, step)
  50.   if end_i == nil then
  51.     end_i   = start_i
  52.     start_i = 1
  53.   end
  54.   step = step or 1
  55.   local range_iter = coroutine.wrap(function()
  56.     for i=start_i, end_i, step do
  57.       coroutine.yield(i)
  58.     end
  59.   end)
  60.   return Underscore:new(range_iter)
  61. end
  62.  
  63. --- Identity function. This function looks useless, but is used throughout Underscore as a default.
  64. -- @name _.identity
  65. -- @param value any object
  66. -- @return value
  67. -- @usage _.identity("foo")
  68. -- => "foo"
  69. function Underscore.identity(value)
  70.   return value
  71. end
  72.  
  73. -- chaining
  74.  
  75. function Underscore:chain()
  76.   self.chained = true
  77.   return self
  78. end
  79.  
  80. function Underscore:value()
  81.   return self._val
  82. end
  83.  
  84. -- iter
  85.  
  86. function Underscore.funcs.each(list, func)
  87.   for i in Underscore.iter(list) do
  88.     func(i)
  89.   end
  90.   return list
  91. end
  92.  
  93. function Underscore.funcs.map(list, func)
  94.   local mapped = {}
  95.   for i in Underscore.iter(list) do
  96.     mapped[#mapped+1] = func(i)
  97.   end
  98.   return mapped
  99. end
  100.  
  101. function Underscore.funcs.reduce(list, memo, func)  
  102.   for i in Underscore.iter(list) do
  103.     memo = func(memo, i)
  104.   end
  105.   return memo
  106. end
  107.  
  108. function Underscore.funcs.detect(list, func)
  109.   for i in Underscore.iter(list) do
  110.     if func(i) then return i end
  111.   end
  112.   return nil  
  113. end
  114.  
  115. function Underscore.funcs.select(list, func)
  116.   local selected = {}
  117.   for i in Underscore.iter(list) do
  118.     if func(i) then selected[#selected+1] = i end
  119.   end
  120.   return selected
  121. end
  122.  
  123. function Underscore.funcs.reject(list, func)
  124.   local selected = {}
  125.   for i in Underscore.iter(list) do
  126.     if not func(i) then selected[#selected+1] = i end
  127.   end
  128.   return selected
  129. end
  130.  
  131. function Underscore.funcs.all(list, func)
  132.   func = func or Underscore.identity
  133.  
  134.   -- TODO what should happen with an empty list?
  135.   for i in Underscore.iter(list) do
  136.     if not func(i) then return false end
  137.   end
  138.   return true
  139. end
  140.  
  141. function Underscore.funcs.none(list, func)
  142.   return not Underscore.funcs.all(list, func)
  143. end
  144.  
  145. function Underscore.funcs.any(list, func)
  146.   func = func or Underscore.identity
  147.  
  148.   -- TODO what should happen with an empty list?  
  149.   for i in Underscore.iter(list) do
  150.     if func(i) then return true end
  151.   end
  152.   return false
  153. end
  154.  
  155. function Underscore.funcs.include(list, value)
  156.   for i in Underscore.iter(list) do
  157.     if i == value then return true end
  158.   end
  159.   return false
  160. end
  161.  
  162. function Underscore.funcs.invoke(list, function_name, ...)
  163.   local args = {...}
  164.   Underscore.funcs.each(list, function(i) i[function_name](i, unpack(args)) end)
  165.   return list
  166. end
  167.  
  168. function Underscore.funcs.pluck(list, propertyName)
  169.   return Underscore.funcs.map(list, function(i) return i[propertyName] end)
  170. end
  171.  
  172. function Underscore.funcs.min(list, func)
  173.   func = func or Underscore.identity
  174.  
  175.   return Underscore.funcs.reduce(list, { item = nil, value = nil }, function(min, item)
  176.     if min.item == nil then
  177.       min.item  = item
  178.       min.value = func(item)
  179.     else
  180.       local value = func(item)
  181.  
  182.       if value < min.value then
  183.         min.item  = item
  184.         min.value = value
  185.       end
  186.     end
  187.  
  188.     return min
  189.   end).item
  190. end
  191.  
  192. function Underscore.funcs.max(list, func)
  193.   func = func or Underscore.identity
  194.  
  195.   return Underscore.funcs.reduce(list, { item = nil, value = nil }, function(max, item)
  196.     if max.item == nil then
  197.       max.item  = item
  198.       max.value = func(item)
  199.     else
  200.       local value = func(item)
  201.       if value > max.value then
  202.         max.item  = item
  203.         max.value = value
  204.       end
  205.     end
  206.     return max
  207.   end).item
  208. end
  209.  
  210. function Underscore.funcs.to_array(list)
  211.   local array = {}
  212.   for i in Underscore.iter(list) do
  213.     array[#array+1] = i
  214.   end
  215.   return array
  216. end
  217.  
  218. function Underscore.funcs.reverse(list)
  219.   local reversed = {}
  220.   for i in Underscore.iter(list) do
  221.     table.insert(reversed, 1, i)
  222.   end
  223.   return reversed
  224. end
  225.  
  226. function Underscore.funcs.sort(iter, comparison_func)
  227.   local array = iter
  228.   if type(iter) == "function" then
  229.     array = Underscore.funcs.to_array(iter)
  230.   end
  231.   table.sort(array, comparison_func)
  232.   return array
  233. end
  234.  
  235. -- arrays
  236.  
  237. function Underscore.funcs.first(array, n)
  238.   if n == nil then
  239.     return array[1]
  240.   else
  241.     local first = {}
  242.     n = math.min(n,#array)
  243.     for i=1,n do
  244.       first[i] = array[i]    
  245.     end
  246.     return first
  247.   end
  248. end
  249.  
  250. function Underscore.funcs.rest(array, index)
  251.   index = index or 2
  252.   local rest = {}
  253.   for i=index,#array do
  254.     rest[#rest+1] = array[i]
  255.   end
  256.   return rest
  257. end
  258.  
  259. function Underscore.funcs.slice(array, start_index, length)
  260.   local sliced_array = {}
  261.  
  262.   start_index = math.max(start_index, 1)
  263.   local end_index = math.min(start_index+length-1, #array)
  264.   for i=start_index, end_index do
  265.     sliced_array[#sliced_array+1] = array[i]
  266.   end
  267.   return sliced_array
  268. end
  269.  
  270. function Underscore.funcs.flatten(array)
  271.   local all = {}
  272.  
  273.   for ele in Underscore.iter(array) do
  274.     if type(ele) == "table" then
  275.       local flattened_element = Underscore.funcs.flatten(ele)
  276.       Underscore.funcs.each(flattened_element, function(e) all[#all+1] = e end)
  277.     else
  278.       all[#all+1] = ele
  279.     end
  280.   end
  281.   return all
  282. end
  283.  
  284. function Underscore.funcs.push(array, item)
  285.   table.insert(array, item)
  286.   return array
  287. end
  288.  
  289. function Underscore.funcs.pop(array)
  290.   return table.remove(array)
  291. end
  292.  
  293. function Underscore.funcs.shift(array)
  294.   return table.remove(array, 1)
  295. end
  296.  
  297. function Underscore.funcs.unshift(array, item)
  298.   table.insert(array, 1, item)
  299.   return array
  300. end
  301.  
  302. function Underscore.funcs.join(array, separator)
  303.   return table.concat(array, separator)
  304. end
  305.  
  306. -- objects
  307.  
  308. function Underscore.funcs.keys(obj)
  309.   local keys = {}
  310.   for k,v in pairs(obj) do
  311.     keys[#keys+1] = k
  312.   end
  313.   return keys
  314. end
  315.  
  316. function Underscore.funcs.values(obj)
  317.   local values = {}
  318.   for k,v in pairs(obj) do
  319.     values[#values+1] = v
  320.   end
  321.   return values
  322. end
  323.  
  324. function Underscore.funcs.extend(destination, source)
  325.   for k,v in pairs(source) do
  326.     destination[k] = v
  327.   end
  328.   return destination
  329. end
  330.  
  331. function Underscore.funcs.is_empty(obj)
  332.   return next(obj) == nil
  333. end
  334.  
  335. -- Originally based on penlight's deepcompare() -- http://luaforge.net/projects/penlight/
  336. function Underscore.funcs.is_equal(o1, o2, ignore_mt)
  337.   local ty1 = type(o1)
  338.   local ty2 = type(o2)
  339.   if ty1 ~= ty2 then return false end
  340.  
  341.   -- non-table types can be directly compared
  342.   if ty1 ~= 'table' then return o1 == o2 end
  343.  
  344.   -- as well as tables which have the metamethod __eq
  345.   local mt = getmetatable(o1)
  346.   if not ignore_mt and mt and mt.__eq then return o1 == o2 end
  347.  
  348.   local is_equal = Underscore.funcs.is_equal
  349.  
  350.   for k1,v1 in pairs(o1) do
  351.     local v2 = o2[k1]
  352.     if v2 == nil or not is_equal(v1,v2, ignore_mt) then return false end
  353.   end
  354.   for k2,v2 in pairs(o2) do
  355.     local v1 = o1[k2]
  356.     if v1 == nil then return false end
  357.   end
  358.   return true
  359. end
  360.  
  361. -- functions
  362.  
  363. function Underscore.funcs.compose(...)
  364.   local function call_funcs(funcs, ...)
  365.     if #funcs > 1 then
  366.       return funcs[1](call_funcs(_.rest(funcs), ...))
  367.     else
  368.       return funcs[1](...)
  369.     end
  370.   end
  371.  
  372.   local funcs = {...}
  373.   return function(...)
  374.     return call_funcs(funcs, ...)
  375.   end
  376. end
  377.  
  378. function Underscore.funcs.wrap(func, wrapper)
  379.   return function(...)
  380.     return wrapper(func, ...)
  381.   end
  382. end
  383.  
  384. function Underscore.funcs.curry(func, argument)
  385.   return function(...)
  386.     return func(argument, ...)
  387.   end
  388. end
  389.  
  390. function Underscore.functions()
  391.   return Underscore.keys(Underscore.funcs)
  392. end
  393.  
  394. -- add aliases
  395. Underscore.methods = Underscore.functions
  396.  
  397. Underscore.funcs.for_each = Underscore.funcs.each
  398. Underscore.funcs.collect  = Underscore.funcs.map
  399. Underscore.funcs.inject   = Underscore.funcs.reduce
  400. Underscore.funcs.foldl    = Underscore.funcs.reduce
  401. Underscore.funcs.filter   = Underscore.funcs.select
  402. Underscore.funcs.every    = Underscore.funcs.all
  403. Underscore.funcs.some     = Underscore.funcs.any
  404. Underscore.funcs.head     = Underscore.funcs.first
  405. Underscore.funcs.tail     = Underscore.funcs.rest
  406.  
  407. local function wrap_functions_for_oo_support()
  408.   local function value_and_chained(value_or_self)
  409.     local chained = false
  410.  
  411.     if getmetatable(value_or_self) == Underscore then
  412.       chained       = value_or_self.chained
  413.       value_or_self = value_or_self._val
  414.     end
  415.  
  416.     return value_or_self, chained
  417.   end
  418.  
  419.   local function value_or_wrap(value, chained)
  420.     if chained then value = Underscore:new(value, true) end
  421.     return value
  422.   end
  423.  
  424.   for fn, func in pairs(Underscore.funcs) do
  425.     Underscore[fn] = function(obj_or_self, ...)
  426.       local obj, chained = value_and_chained(obj_or_self)
  427.       return value_or_wrap(func(obj, ...), chained)  
  428.     end  
  429.   end
  430. end
  431.  
  432. wrap_functions_for_oo_support()
  433.  
  434. return Underscore:new()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement