Robert Klemme
2/3/2006 3:19:00 PM
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