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