/zoeplat

To get this branch, use:
bzr branch http://9ix.org/bzr/zoeplat

« back to all changes in this revision

Viewing changes to underscore.lua

  • Committer: Josh C
  • Date: 2013-03-02 20:40:57 UTC
  • Revision ID: josh@9ix.org-20130302204057-yrra0a51zgtpq2v2
zoetrope 1.3.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
 
2
 
--- Underscore is a set of utility functions for dealing with 
3
 
 
4
 
local Underscore = { funcs = {} }
5
 
Underscore.__index = Underscore
6
 
 
7
 
function Underscore.__call(_, value)
8
 
        return Underscore:new(value)
9
 
end
10
 
 
11
 
function Underscore:new(value, chained)
12
 
        return setmetatable({ _val = value, chained = chained or false }, self)
13
 
end
14
 
 
15
 
function Underscore.iter(list_or_iter)
16
 
        if type(list_or_iter) == "function" then return list_or_iter end
17
 
        
18
 
        return coroutine.wrap(function() 
19
 
                for i=1,#list_or_iter do
20
 
                        coroutine.yield(list_or_iter[i])
21
 
                end
22
 
        end)
23
 
end
24
 
 
25
 
function Underscore.range(start_i, end_i, step)
26
 
        if end_i == nil then
27
 
                end_i = start_i
28
 
                start_i = 1
29
 
        end
30
 
        step = step or 1
31
 
        local range_iter = coroutine.wrap(function() 
32
 
                for i=start_i, end_i, step do
33
 
                        coroutine.yield(i)
34
 
                end
35
 
        end)
36
 
        return Underscore:new(range_iter)
37
 
end
38
 
 
39
 
--- Identity function. This function looks useless, but is used throughout Underscore as a default.
40
 
function Underscore.identity(value)
41
 
        return value
42
 
end
43
 
 
44
 
 
45
 
function Underscore:chain()
46
 
        self.chained = true
47
 
        return self
48
 
end
49
 
 
50
 
function Underscore:value()
51
 
        return self._val
52
 
end
53
 
 
54
 
 
55
 
function Underscore.funcs.each(list, func)
56
 
        for i in Underscore.iter(list) do
57
 
                func(i)
58
 
        end
59
 
        return list
60
 
end
61
 
 
62
 
function Underscore.funcs.map(list, func)
63
 
        local mapped = {}
64
 
        for i in Underscore.iter(list) do
65
 
                mapped[#mapped+1] = func(i)
66
 
        end     
67
 
        return mapped
68
 
end
69
 
 
70
 
function Underscore.funcs.reduce(list, memo, func)      
71
 
        for i in Underscore.iter(list) do
72
 
                memo = func(memo, i)
73
 
        end     
74
 
        return memo
75
 
end
76
 
 
77
 
function Underscore.funcs.detect(list, func)
78
 
        for i in Underscore.iter(list) do
79
 
                if func(i) then return i end
80
 
        end     
81
 
        return nil      
82
 
end
83
 
 
84
 
function Underscore.funcs.select(list, func)
85
 
        local selected = {}
86
 
        for i in Underscore.iter(list) do
87
 
                if func(i) then selected[#selected+1] = i end
88
 
        end
89
 
        return selected
90
 
end
91
 
 
92
 
function Underscore.funcs.reject(list, func)
93
 
        local selected = {}
94
 
        for i in Underscore.iter(list) do
95
 
                if not func(i) then selected[#selected+1] = i end
96
 
        end
97
 
        return selected
98
 
end
99
 
 
100
 
function Underscore.funcs.all(list, func)
101
 
        func = func or Underscore.identity
102
 
        
103
 
        -- TODO what should happen with an empty list?
104
 
        for i in Underscore.iter(list) do
105
 
                if not func(i) then return false end
106
 
        end
107
 
        return true
108
 
end
109
 
 
110
 
function Underscore.funcs.any(list, func)
111
 
        func = func or Underscore.identity
112
 
 
113
 
        -- TODO what should happen with an empty list?  
114
 
        for i in Underscore.iter(list) do
115
 
                if func(i) then return true end
116
 
        end     
117
 
        return false
118
 
end
119
 
 
120
 
function Underscore.funcs.include(list, value)
121
 
        for i in Underscore.iter(list) do
122
 
                if i == value then return true end
123
 
        end     
124
 
        return false
125
 
end
126
 
 
127
 
function Underscore.funcs.invoke(list, function_name, ...)
128
 
        local args = {...}
129
 
        Underscore.funcs.each(list, function(i) i[function_name](i, unpack(args)) end)
130
 
        return list
131
 
end
132
 
 
133
 
function Underscore.funcs.pluck(list, propertyName)
134
 
        return Underscore.funcs.map(list, function(i) return i[propertyName] end)
135
 
end
136
 
 
137
 
function Underscore.funcs.min(list, func)
138
 
        func = func or Underscore.identity
139
 
        
140
 
        return Underscore.funcs.reduce(list, { item = nil, value = nil }, function(min, item) 
141
 
                if min.item == nil then
142
 
                        min.item = item
143
 
                        min.value = func(item)
144
 
                else
145
 
                        local value = func(item)
146
 
                        if value < min.value then
147
 
                                min.item = item
148
 
                                min.value = value
149
 
                        end
150
 
                end
151
 
                return min
152
 
        end).item
153
 
end
154
 
 
155
 
function Underscore.funcs.max(list, func)
156
 
        func = func or Underscore.identity
157
 
        
158
 
        return Underscore.funcs.reduce(list, { item = nil, value = nil }, function(max, item) 
159
 
                if max.item == nil then
160
 
                        max.item = item
161
 
                        max.value = func(item)
162
 
                else
163
 
                        local value = func(item)
164
 
                        if value > max.value then
165
 
                                max.item = item
166
 
                                max.value = value
167
 
                        end
168
 
                end
169
 
                return max
170
 
        end).item
171
 
end
172
 
 
173
 
function Underscore.funcs.to_array(list)
174
 
        local array = {}
175
 
        for i in Underscore.iter(list) do
176
 
                array[#array+1] = i
177
 
        end     
178
 
        return array
179
 
end
180
 
 
181
 
function Underscore.funcs.reverse(list)
182
 
        local reversed = {}
183
 
        for i in Underscore.iter(list) do
184
 
                table.insert(reversed, 1, i)
185
 
        end     
186
 
        return reversed
187
 
end
188
 
 
189
 
function Underscore.funcs.sort(iter, comparison_func)
190
 
        local array = iter
191
 
        if type(iter) == "function" then
192
 
                array = Underscore.funcs.to_array(iter)
193
 
        end
194
 
        table.sort(array, comparison_func)
195
 
        return array
196
 
end
197
 
 
198
 
 
199
 
function Underscore.funcs.first(array, n)
200
 
        if n == nil then
201
 
                return array[1]
202
 
        else
203
 
                local first = {}
204
 
                n = math.min(n,#array)
205
 
                for i=1,n do
206
 
                        first[i] = array[i]                     
207
 
                end
208
 
                return first
209
 
        end
210
 
end
211
 
 
212
 
function Underscore.funcs.rest(array, index)
213
 
        index = index or 2
214
 
        local rest = {}
215
 
        for i=index,#array do
216
 
                rest[#rest+1] = array[i]
217
 
        end
218
 
        return rest
219
 
end
220
 
 
221
 
function Underscore.funcs.slice(array, start_index, length)
222
 
        local sliced_array = {}
223
 
        
224
 
        start_index = math.max(start_index, 1)
225
 
        local end_index = math.min(start_index+length-1, #array)
226
 
        for i=start_index, end_index do
227
 
                sliced_array[#sliced_array+1] = array[i]
228
 
        end
229
 
        return sliced_array
230
 
end
231
 
 
232
 
function Underscore.funcs.flatten(array)
233
 
        local all = {}
234
 
        
235
 
        for ele in Underscore.iter(array) do
236
 
                if type(ele) == "table" then
237
 
                        local flattened_element = Underscore.funcs.flatten(ele)
238
 
                        Underscore.funcs.each(flattened_element, function(e) all[#all+1] = e end)
239
 
                else
240
 
                        all[#all+1] = ele
241
 
                end
242
 
        end
243
 
        return all
244
 
end
245
 
 
246
 
function Underscore.funcs.push(array, item)
247
 
        table.insert(array, item)
248
 
        return array
249
 
end
250
 
 
251
 
function Underscore.funcs.pop(array)
252
 
        return table.remove(array)
253
 
end
254
 
 
255
 
function Underscore.funcs.shift(array)
256
 
        return table.remove(array, 1)
257
 
end
258
 
 
259
 
function Underscore.funcs.unshift(array, item)
260
 
        table.insert(array, 1, item)
261
 
        return array
262
 
end
263
 
 
264
 
function Underscore.funcs.join(array, separator)
265
 
        return table.concat(array, separator)
266
 
end
267
 
 
268
 
 
269
 
function Underscore.funcs.keys(obj)
270
 
        local keys = {}
271
 
        for k,v in pairs(obj) do
272
 
                keys[#keys+1] = k
273
 
        end
274
 
        return keys
275
 
end
276
 
 
277
 
function Underscore.funcs.values(obj)
278
 
        local values = {}
279
 
        for k,v in pairs(obj) do
280
 
                values[#values+1] = v
281
 
        end
282
 
        return values
283
 
end
284
 
 
285
 
function Underscore.funcs.extend(destination, source)
286
 
        for k,v in pairs(source) do
287
 
                destination[k] = v
288
 
        end     
289
 
        return destination
290
 
end
291
 
 
292
 
function Underscore.funcs.is_empty(obj)
293
 
        return next(obj) == nil
294
 
end
295
 
 
296
 
function Underscore.funcs.is_equal(o1, o2, ignore_mt)
297
 
        local ty1 = type(o1)
298
 
        local ty2 = type(o2)
299
 
        if ty1 ~= ty2 then return false end
300
 
        
301
 
        -- non-table types can be directly compared
302
 
        if ty1 ~= 'table' then return o1 == o2 end
303
 
        
304
 
        -- as well as tables which have the metamethod __eq
305
 
        local mt = getmetatable(o1)
306
 
        if not ignore_mt and mt and mt.__eq then return o1 == o2 end
307
 
        
308
 
        local is_equal = Underscore.funcs.is_equal
309
 
        
310
 
        for k1,v1 in pairs(o1) do
311
 
                local v2 = o2[k1]
312
 
                if v2 == nil or not is_equal(v1,v2, ignore_mt) then return false end
313
 
        end
314
 
        for k2,v2 in pairs(o2) do
315
 
                local v1 = o1[k2]
316
 
                if v1 == nil then return false end
317
 
        end
318
 
        return true
319
 
end
320
 
 
321
 
 
322
 
function Underscore.funcs.compose(...)
323
 
        local function call_funcs(funcs, ...)
324
 
                if #funcs > 1 then
325
 
                        return funcs[1](call_funcs(_.rest(funcs), ...))
326
 
                else
327
 
                        return funcs[1](...)
328
 
                end
329
 
        end
330
 
        
331
 
        local funcs = {...}
332
 
        return function(...)
333
 
                return call_funcs(funcs, ...)
334
 
        end
335
 
end
336
 
 
337
 
function Underscore.funcs.wrap(func, wrapper)
338
 
        return function(...)
339
 
                return wrapper(func, ...)
340
 
        end
341
 
end
342
 
 
343
 
function Underscore.funcs.curry(func, argument)
344
 
        return function(...)
345
 
                return func(argument, ...)
346
 
        end
347
 
end
348
 
 
349
 
function Underscore.functions() 
350
 
        return Underscore.keys(Underscore.funcs)
351
 
end
352
 
 
353
 
Underscore.methods = Underscore.functions
354
 
 
355
 
Underscore.funcs.for_each = Underscore.funcs.each
356
 
Underscore.funcs.collect = Underscore.funcs.map
357
 
Underscore.funcs.inject = Underscore.funcs.reduce
358
 
Underscore.funcs.foldl = Underscore.funcs.reduce
359
 
Underscore.funcs.filter = Underscore.funcs.select
360
 
Underscore.funcs.every = Underscore.funcs.all
361
 
Underscore.funcs.some = Underscore.funcs.any
362
 
Underscore.funcs.head = Underscore.funcs.first
363
 
Underscore.funcs.tail = Underscore.funcs.rest
364
 
 
365
 
local function wrap_functions_for_oo_support()
366
 
        local function value_and_chained(value_or_self)
367
 
                local chained = false
368
 
                if getmetatable(value_or_self) == Underscore then 
369
 
                        chained = value_or_self.chained
370
 
                        value_or_self = value_or_self._val 
371
 
                end
372
 
                return value_or_self, chained
373
 
        end
374
 
 
375
 
        local function value_or_wrap(value, chained)
376
 
                if chained then value = Underscore:new(value, true) end
377
 
                return value
378
 
        end
379
 
 
380
 
        for fn, func in pairs(Underscore.funcs) do
381
 
                Underscore[fn] = function(obj_or_self, ...)
382
 
                        local obj, chained = value_and_chained(obj_or_self)     
383
 
                        return value_or_wrap(func(obj, ...), chained)           
384
 
                end      
385
 
        end
386
 
end
387
 
 
388
 
wrap_functions_for_oo_support()
389
 
 
390
 
return Underscore:new()