# Allow additional parameters to Enumerable#inject

**URL:** <https://www.ruby-forum.com/t/allow-additional-parameters-to-enumerable-inject/53389>\
**Category:** Ruby\
**Created:** [February 3, 2006, 4:00pm UTC](https://www.ruby-forum.com/t/allow-additional-parameters-to-enumerable-inject/53389 "2006-02-03T16:00:24Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Levin\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/l/eada6e/32.png) [@Levin\_A](https://www.ruby-forum.com/u/Levin_A)\
**Post date:** [February 3, 2006, 4:00pm UTC](https://www.ruby-forum.com/t/allow-additional-parameters-to-enumerable-inject/53389/1 "2006-02-03T16:00:24Z")

</div>

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?

Viele GrÃ¼Ã?e,  
Levin

---

<div class="post-metadata">

**Author:** ![Levin\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/l/eada6e/32.png) [@Levin\_A](https://www.ruby-forum.com/u/Levin_A)\
**Post date:** [February 3, 2006, 4:18pm UTC](https://www.ruby-forum.com/t/allow-additional-parameters-to-enumerable-inject/53389/2 "2006-02-03T16:18:40Z")

</div>

An array can be used as a workaround:

[1,2,3].inject([[],arg]) { |(a,b), elem|  
…  
[a,arg]  
}

lopex

---

<div class="post-metadata">

**Author:** ![Levin\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/l/eada6e/32.png) [@Levin\_A](https://www.ruby-forum.com/u/Levin_A)\
**Post date:** [February 3, 2006, 4:24pm UTC](https://www.ruby-forum.com/t/allow-additional-parameters-to-enumerable-inject/53389/3 "2006-02-03T16:24:43Z")

</div>

Levin A. wrote:

> ```
> if last_seen == elem then a[-1] += 1 else a << 1 end
> # works just like regular inject, but passes the additional
> 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
```

---

<div class="post-metadata">

**Author:** ![Levin\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/l/eada6e/32.png) [@Levin\_A](https://www.ruby-forum.com/u/Levin_A)\
**Post date:** [February 3, 2006, 5:10pm UTC](https://www.ruby-forum.com/t/allow-additional-parameters-to-enumerable-inject/53389/4 "2006-02-03T17:10:02Z")

</div>

On 2/3/06, Robert K. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> > 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|

Thanks, I was not aware that parameters can be grouped like that.

But I think that an Enumerable#inject with additional parameters would  
also be useful for many other things, like removing duplicates:

def remove\_duplicates1(enum)  
enum.inject([[], nil]) {|(arr, last),elem| arr \<\< elem unless elem  
== last; [arr, elem] }[0]  
end  
def remove\_duplicates2(enum)  
enum.inject([], nil) {|(arr, elem, last| arr \<\< elem unless elem  
== last; [arr, elem] }  
end

The second one seems looks a lot cleaner.

> Also, I’m not sure it’s a good idea to put this method in Enumerable. Is  
> it really general enough?

No, it probably is not. This was just a for small experiment (\*), i  
would not put this into a library.  
(However, it seems to be preferred to put methods into the class  
“where they belong” in Ruby instead of using helper classes)

Thank you,  
Levin

(\*) [http://www.rexswain.com/benford.html](http://www.rexswain.com/benford.html)  
“…It showed, he said, that the overwhelming odds are that at some  
point in a series of 200 tosses, either heads or tails will come up  
six or more times in a row.”

(0…10\_000).select {  
(0…200).map { rand 2 }.inject([[],nil]) {|(a,last),e|  
if e==last then a[-1]+=1 else a \<\< 1 end  
[a,e]  
}[0].partition { |e| e \>= 6 }.first.empty?  
}.length / 10\_000.0  
#=\> 0.0349

“overwhelming odds” → 97%

---

<div class="post-metadata">

**Author:** ![Levin\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/l/eada6e/32.png) [@Levin\_A](https://www.ruby-forum.com/u/Levin_A)\
**Post date:** [February 3, 2006, 6:10pm UTC](https://www.ruby-forum.com/t/allow-additional-parameters-to-enumerable-inject/53389/5 "2006-02-03T18:10:18Z")

</div>

Not really about inject specifically… just attempting another way

a = [1,1,1,2,2,2,2,3,4,4,4,4]

a.zip([0] + a).map { |i, j| i - j }.inject([]) do |s, k|  
k.zero? ? s[0…-1] + [s[-1] + 1] : s \<\< k  
end
