improve Enumerable#reverse_each to be efficient
[mruby.git] / mrbgems / mruby-enum-ext / mrblib / enum.rb
blob0ce1d760558ae90d4967dfb89bee0ec7700f7a9a
1 ##
2 # Enumerable
4 module Enumerable
5   ##
6   # call-seq:
7   #    enum.drop(n)               -> array
8   #
9   # Drops first n elements from <i>enum</i>, and returns rest elements
10   # in an array.
11   #
12   #    a = [1, 2, 3, 4, 5, 0]
13   #    a.drop(3)             #=> [4, 5, 0]
15   def drop(n)
16     raise TypeError, "expected Integer for 1st argument" unless n.kind_of? Integer
17     raise ArgumentError, "attempt to drop negative size" if n < 0
19     ary = []
20     self.each {|*val| n == 0 ? ary << val.__svalue : n -= 1 }
21     ary
22   end
24   ##
25   # call-seq:
26   #    enum.drop_while {|arr| block }   -> array
27   #
28   # Drops elements up to, but not including, the first element for
29   # which the block returns +nil+ or +false+ and returns an array
30   # containing the remaining elements.
31   #
32   #    a = [1, 2, 3, 4, 5, 0]
33   #    a.drop_while {|i| i < 3 }   #=> [3, 4, 5, 0]
35   def drop_while(&block)
36     ary, state = [], false
37     self.each do |*val|
38       state = true if !state and !block.call(*val)
39       ary << val.__svalue if state
40     end
41     ary
42   end
44   ##
45   # call-seq:
46   #    enum.take(n)               -> array
47   #
48   # Returns first n elements from <i>enum</i>.
49   #
50   #    a = [1, 2, 3, 4, 5, 0]
51   #    a.take(3)             #=> [1, 2, 3]
53   def take(n)
54     raise TypeError, "expected Integer for 1st argument" unless n.kind_of? Integer
55     raise ArgumentError, "attempt to take negative size" if n < 0
57     ary = []
58     self.each do |*val|
59       break if ary.size >= n
60       ary << val.__svalue
61     end
62     ary
63   end
65   ##
66   # call-seq:
67   #    enum.take_while {|arr| block }   -> array
68   #
69   # Passes elements to the block until the block returns +nil+ or +false+,
70   # then stops iterating and returns an array of all prior elements.
71   #
72   #
73   #    a = [1, 2, 3, 4, 5, 0]
74   #    a.take_while {|i| i < 3 }   #=> [1, 2]
76   def take_while(&block)
77     ary = []
78     self.each do |*val|
79       return ary unless block.call(*val)
80       ary << val.__svalue
81     end
82     ary
83   end
84   
85   ##
86   # call-seq:
87   #   enum.each_cons(n) {...}   ->  nil
88   #
89   # Iterates the given block for each array of consecutive <n>
90   # elements.
91   #
92   # e.g.:
93   #     (1..10).each_cons(3) {|a| p a}
94   #     # outputs below
95   #     [1, 2, 3]
96   #     [2, 3, 4]
97   #     [3, 4, 5]
98   #     [4, 5, 6]
99   #     [5, 6, 7]
100   #     [6, 7, 8]
101   #     [7, 8, 9]
102   #     [8, 9, 10]
104   def each_cons(n, &block)
105     raise TypeError, "expected Integer for 1st argument" unless n.kind_of? Integer
106     raise ArgumentError, "invalid size" if n <= 0
108     ary = []
109     self.each do |*val|
110       ary.shift if ary.size == n
111       ary << val.__svalue
112       block.call(ary.dup) if ary.size == n
113     end
114   end
116   ##
117   # call-seq:
118   #   enum.each_slice(n) {...}  ->  nil
119   #
120   # Iterates the given block for each slice of <n> elements.
121   #
122   # e.g.:
123   #     (1..10).each_slice(3) {|a| p a}
124   #     # outputs below
125   #     [1, 2, 3]
126   #     [4, 5, 6]
127   #     [7, 8, 9]
128   #     [10]
130   def each_slice(n, &block)
131     raise TypeError, "expected Integer for 1st argument" unless n.kind_of? Integer
132     raise ArgumentError, "invalid slice size" if n <= 0
134     ary = []
135     self.each do |*val|
136       ary << val.__svalue
137       if ary.size == n
138         block.call(ary)
139         ary = []
140       end
141     end
142     block.call(ary) unless ary.empty?
143   end
145   ##
146   # call-seq:
147   #    enum.group_by {| obj | block }  -> a_hash
148   #
149   # Returns a hash, which keys are evaluated result from the
150   # block, and values are arrays of elements in <i>enum</i>
151   # corresponding to the key.
152   #
153   #    (1..6).group_by {|i| i%3}   #=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]}
155   def group_by(&block)
156     h = {}
157     self.each do |*val|
158       key = block.call(*val)
159       sv = val.__svalue
160       h.key?(key) ? (h[key] << sv) : (h[key] = [sv])
161     end
162     h
163   end
165   ##
166   # call-seq:
167   #    enum.sort_by { |obj| block }   -> array
168   #
169   # Sorts <i>enum</i> using a set of keys generated by mapping the
170   # values in <i>enum</i> through the given block.
171   def sort_by(&block)
172     ary = []
173     orig = [] 
174     self.each_with_index{|e, i|
175       orig.push(e)
176       ary.push([block.call(e), i])
177     }
178     if ary.size > 1
179       __sort_sub__(ary, ::Array.new(ary.size), 0, 0, ary.size - 1) do |a,b|
180         a <=> b
181       end
182     end
183     ary.collect{|e,i| orig[i]}
184   end
186   NONE = Object.new
187   ##
188   # call-seq:
189   #    enum.first       ->  obj or nil
190   #    enum.first(n)    ->  an_array
191   #
192   # Returns the first element, or the first +n+ elements, of the enumerable.
193   # If the enumerable is empty, the first form returns <code>nil</code>, and the
194   # second form returns an empty array.
195   def first(n=NONE)
196     if n == NONE
197       self.each do |*val|
198         return val.__svalue
199       end
200       return nil
201     else
202       a = []
203       i = 0
204       self.each do |*val|
205         break if n<=i
206         a.push val.__svalue
207         i += 1
208       end
209       a
210     end
211   end
213   ##
214   # call-seq:
215   #    enum.count                 -> int
216   #    enum.count(item)           -> int
217   #    enum.count { |obj| block } -> int
218   #
219   # Returns the number of items in +enum+ through enumeration.
220   # If an argument is given, the number of items in +enum+ that
221   # are equal to +item+ are counted.  If a block is given, it
222   # counts the number of elements yielding a true value.
223   def count(v=NONE, &block)
224     count = 0
225     if block
226       self.each do |*val|
227         count += 1 if block.call(*val)
228       end
229     else
230       if v == NONE
231         self.each { count += 1 }