From: Robert Klemme Date: 2006-02-04T00:23:18+09:00 Subject: Re: Allow additional parameters to Enumerable#inject Levin Alexander wrote: > Hi, > > I wanted to count consecutive elements in an array: > > [1,1,1,2,2,2,2,3,4,4,4,4].count_streams #=> [3,4,1,4] > > module Enumerable > def count_streams > last_seen = nil > self.inject([]) { |a, elem| > if last_seen == elem then a[-1] += 1 else a << 1 end > last_seen = elem > a > } > end > end > > However, the assignment to "last" outside of the block seems ugly, so > I changed inject to take additional parameters: > > module Enumerable > # works just like regular inject, but passes the additional > parameters # to the block > def inject_with_state(memo, *other) > self.each { |obj| > memo, *other = yield(memo, obj, *other) > } > memo > end > > def count_streams > self.inject_with_state([], nil) { |a, elem, last_seen| > if last_seen == elem then a[-1] += 1 else a << 1 end > [a, elem] > } > end > end > > Is this a good solution to the problem? > > I think that inject_with_state could be made fully backwards > compatible to inject, it would be nice if inject could be changed to > support this. What do you think? You don't need to redefine Enumerable#inject for this. >> def count_streams(enum) >> enum.inject([[],nil]) {|(a, last),e| e == last ? a[-1]+=1 : a<<1; [a, e]}[0] >> end => nil >> count_streams [1,1,1,2,2,2,2,3,4,4,4,4] => [3, 4, 1, 4] You can as well do somehting like this: >> def count_streams_2(enum) >> enum.inject([]) do |a, e| ?> a.empty? || e != a[-1][0] ? a << [e,1] : a[-1][1]+=1 >> a >> end >> end => nil >> count_streams_2 [1,1,1,2,2,2,2,3,4,4,4,4] => [[1, 3], [2, 4], [3, 1], [4, 4]] >> count_streams_2([1,1,1,2,2,2,2,3,4,4,4,4]).map {|a| a[1]} => [3, 4, 1, 4] Also, I'm not sure it's a good idea to put this method in Enumerable. Is it really general enough? Kind regards robert