# Grouping elements of an array

**URL:** <https://rubytalk.org/t/grouping-elements-of-an-array/57797>\
**Category:** ruby-talk\
**Created:** [18 March 2010 22:50 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797 "2010-03-18T22:50:53Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Steve\_Wilhelm](https://avatars.discourse-cdn.com/v4/letter/s/b9bd4f/32.png) [@Steve\_Wilhelm](https://rubytalk.org/u/Steve_Wilhelm)\
**Post date:** [18 March 2010 22:50 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797/1 "2010-03-18T22:50:53Z")

</div>

I have an array of records that contain timestamps at random intervals.  
The records are ordered by timestamp.

I would like to convert the array into an array of arrays; each subarray  
would contain "grouped records." Grouping would occur if the timestamp  
of the next element in the original array is within thirty seconds of  
the current element.

Example (second column is timestamp in seconds starting from zero).

A 0  
B 15  
C 35  
D 100  
E 205  
F 215  
G 300

would result in

[[A, B, C], [D], [E, F], [G]]

Any help on how to do this in the "Ruby Way" would be appreciated.

- Steve W.

> **···**
>
> --  
> Posted via [http://www.ruby-forum.com/](http://www.ruby-forum.com/).

---

<div class="post-metadata">

**Author:** ![Josh\_Cheek](https://avatars.discourse-cdn.com/v4/letter/j/e79b87/32.png) [@Josh\_Cheek](https://rubytalk.org/u/Josh_Cheek)\
**Post date:** [19 March 2010 02:27 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797/2 "2010-03-19T02:27:58Z")

</div>

What about  
A 0  
B 20  
C 40

Does that become  
[[A,B],[B,C]] or [[A,B,C]] or something else? The congruence class here is  
unclear.

> **···**
>
> On Thu, Mar 18, 2010 at 4:50 PM, Steve Wilhelm \<steve@studio831.com\> wrote:
> 
> > I have an array of records that contain timestamps at random intervals.  
> > The records are ordered by timestamp.
> > 
> > I would like to convert the array into an array of arrays; each subarray  
> > would contain "grouped records." Grouping would occur if the timestamp  
> > of the next element in the original array is within thirty seconds of  
> > the current element.
> > 
> > Example (second column is timestamp in seconds starting from zero).
> > 
> > A 0  
> > B 15  
> > C 35  
> > D 100  
> > E 205  
> > F 215  
> > G 300
> > 
> > would result in
> > 
> > [[A, B, C], [D], [E, F], [G]]
> > 
> > Any help on how to do this in the "Ruby Way" would be appreciated.
> > 
> > - Steve W.  
> > --  
> > Posted via [http://www.ruby-forum.com/\](http://www.ruby-forum.com/%5C).

---

<div class="post-metadata">

**Author:** ![Roger\_Braun](https://avatars.discourse-cdn.com/v4/letter/r/e9bcb4/32.png) [@Roger\_Braun](https://rubytalk.org/u/Roger_Braun)\
**Post date:** [19 March 2010 03:16 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797/3 "2010-03-19T03:16:30Z")

</div>

Hi

> I have an array of records that contain timestamps at random intervals.  
> The records are ordered by timestamp.
> 
> I would like to convert the array into an array of arrays; each subarray  
> would contain "grouped records." Grouping would occur if the timestamp  
> of the next element in the original array is within thirty seconds of  
> the current element.
> 
> Example (second column is timestamp in seconds starting from zero).
> 
> A 0  
> B 15  
> C 35  
> D 100  
> E 205  
> F 215  
> G 300
> 
> would result in
> 
> [[A, B, C], [D], [E, F], [G]]
> 
> Any help on how to do this in the "Ruby Way" would be appreciated.

How about this:

&nbsp;&nbsp;1 arr = [0,15,35,100,205,300]  
&nbsp;&nbsp;2 arr2 = [0, 20, 40]  
&nbsp;&nbsp;3  
&nbsp;&nbsp;4 def group(array)  
&nbsp;&nbsp;5 array.map!{|e| [e]}  
&nbsp;&nbsp;6 array.inject() do |r, e|  
&nbsp;&nbsp;7 if r == or e[0] - r.last.last \> 30 then  
&nbsp;&nbsp;8 r.push(e)  
&nbsp;&nbsp;9 else  
10 r[-1].push(e[0])  
11 end  
12 r  
13 end  
14 end  
15  
16 puts arr.inspect  
17 puts group(arr).inspect  
18 puts arr2.inspect  
19 puts group(arr2).inspect

> **···**
>
> On Thu, Mar 18, 2010 at 11:50 PM, Steve Wilhelm \<steve@studio831.com\> wrote:
> 
> --  
> Roger Braun  
> [http://yononaka.de](http://yononaka.de)  
> roger.braun@student.uni-tuebingen.de

---

<div class="post-metadata">

**Author:** ![Josh\_Cheek](https://avatars.discourse-cdn.com/v4/letter/j/e79b87/32.png) [@Josh\_Cheek](https://rubytalk.org/u/Josh_Cheek)\
**Post date:** [19 March 2010 03:29 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797/4 "2010-03-19T03:29:42Z")

</div>

Here is my solution, it's conceptually similar to Roger's, though differs in  
implementation [gist:337195 · GitHub](http://gist.github.com/337195)

> **···**
>
> On Thu, Mar 18, 2010 at 4:50 PM, Steve Wilhelm \<steve@studio831.com\> wrote:
> 
> > I have an array of records that contain timestamps at random intervals.  
> > The records are ordered by timestamp.
> > 
> > I would like to convert the array into an array of arrays; each subarray  
> > would contain "grouped records." Grouping would occur if the timestamp  
> > of the next element in the original array is within thirty seconds of  
> > the current element.
> > 
> > Example (second column is timestamp in seconds starting from zero).
> > 
> > A 0  
> > B 15  
> > C 35  
> > D 100  
> > E 205  
> > F 215  
> > G 300
> > 
> > would result in
> > 
> > [[A, B, C], [D], [E, F], [G]]
> > 
> > Any help on how to do this in the "Ruby Way" would be appreciated.
> > 
> > - Steve W.  
> > --  
> > Posted via [http://www.ruby-forum.com/\](http://www.ruby-forum.com/%5C).

---

<div class="post-metadata">

**Author:** ![1119](https://avatars.discourse-cdn.com/v4/letter/1/dfb087/32.png) [@1119](https://rubytalk.org/u/1119)\
**Post date:** [19 March 2010 09:08 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797/5 "2010-03-19T09:08:51Z")

</div>

Hello, I am new to Ruby. Below is my solution:

a=[0, 15, 35, 100, 205, 215, 300]  
b=[]  
c=[]  
d=a[0]  
a.each do |i|  
&nbsp;&nbsp;if i - d \< 30  
&nbsp;&nbsp;&nbsp;&nbsp;c \<\< i  
&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;b \<\< c  
&nbsp;&nbsp;&nbsp;&nbsp;c=[i]  
&nbsp;&nbsp;end  
&nbsp;&nbsp;d =i  
end  
if c.size \> 0  
&nbsp;&nbsp;b \<\< c  
end

p b

---

<div class="post-metadata">

**Author:** ![Brabuhr](https://avatars.discourse-cdn.com/v4/letter/b/919ad9/32.png) [@Brabuhr](https://rubytalk.org/u/Brabuhr)\
**Post date:** [20 March 2010 00:03 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797/6 "2010-03-20T00:03:57Z")

</div>

Here's another way:

require 'pp'  
require 'set'

Struct.new("Record", :value, :timestamp)

data = [  
&nbsp;&nbsp;Struct::Record.new('A', 0),  
&nbsp;&nbsp;Struct::Record.new('B', 15),  
&nbsp;&nbsp;Struct::Record.new('C', 35),  
&nbsp;&nbsp;Struct::Record.new('D', 100),  
&nbsp;&nbsp;Struct::Record.new('E', 205),  
&nbsp;&nbsp;Struct::Record.new('F', 215),  
&nbsp;&nbsp;Struct::Record.new('G', 300),  
]

pp data

pp data.  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to\_set.  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;divide{|i, j| (i.timestamp - j.timestamp.abs) \< 30}.  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;map{|s| s.to\_a}

$ ruby -v z.rb  
ruby 1.8.7 (2008-08-11 patchlevel 72) [universal-darwin10.0]  
[#\<struct Struct::Record value="A", timestamp=0\>,  
#\<struct Struct::Record value="B", timestamp=15\>,  
#\<struct Struct::Record value="C", timestamp=35\>,  
#\<struct Struct::Record value="D", timestamp=100\>,  
#\<struct Struct::Record value="E", timestamp=205\>,  
#\<struct Struct::Record value="F", timestamp=215\>,  
#\<struct Struct::Record value="G", timestamp=300\>]  
[[#\<struct Struct::Record value="D", timestamp=100\>],  
[#\<struct Struct::Record value="G", timestamp=300\>],  
[#\<struct Struct::Record value="F", timestamp=215\>,  
&nbsp;&nbsp;#\<struct Struct::Record value="E", timestamp=205\>],  
[#\<struct Struct::Record value="A", timestamp=0\>,  
&nbsp;&nbsp;#\<struct Struct::Record value="C", timestamp=35\>,  
&nbsp;&nbsp;#\<struct Struct::Record value="B", timestamp=15\>]]

(Depending on the amount of data converting from array to sets to  
arrays may be expensive 🙂

> **···**
>
> On Thu, Mar 18, 2010 at 6:50 PM, Steve Wilhelm \<steve@studio831.com\> wrote:
> 
> > I have an array of records that contain timestamps at random intervals.  
> > The records are ordered by timestamp.
> > 
> > I would like to convert the array into an array of arrays; each subarray  
> > would contain "grouped records." Grouping would occur if the timestamp  
> > of the next element in the original array is within thirty seconds of  
> > the current element.
> > 
> > Example (second column is timestamp in seconds starting from zero).
> > 
> > A 0, B 15, C 35, D 100, E 205, F 215, G 300
> > 
> > would result in
> > 
> > [[A, B, C], [D], [E, F], [G]]
> > 
> > Any help on how to do this in the "Ruby Way" would be appreciated.

---

<div class="post-metadata">

**Author:** ![Robert\_K1](https://yyz1.discourse-cdn.com/flex029/user_avatar/rubytalk.org/robert_k1/32/1830_2.png) [@Robert\_K1](https://rubytalk.org/u/Robert_K1)\
**Post date:** [20 March 2010 12:41 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797/7 "2010-03-20T12:41:28Z")

</div>

Assuming records are ordered already - otherwise you need a sort in between.

require 'pp'

dat = \<\<DDD.each\_line.map {|l|r = l.split;r[1]=r[1].to\_i;r}  
A 0  
B 15  
C 35  
D 100  
E 205  
F 215  
G 300  
DDD

pp dat

gr = dat.inject do |agg, rec|  
&nbsp;&nbsp;&nbsp;if agg.last && rec[1] - agg.last.last[1] \<= 15  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;agg.last \<\< rec  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;agg  
&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;agg \<\< [rec]  
&nbsp;&nbsp;&nbsp;end  
end

pp gr

Note: I don't claim that this is \*the\* Ruby way.

Kind regards

&nbsp;&nbsp;robert

> **···**
>
> On 03/18/2010 11:50 PM, Steve Wilhelm wrote:
> 
> > I have an array of records that contain timestamps at random intervals.  
> > The records are ordered by timestamp.
> > 
> > I would like to convert the array into an array of arrays; each subarray  
> > would contain "grouped records." Grouping would occur if the timestamp  
> > of the next element in the original array is within thirty seconds of  
> > the current element.
> > 
> > Example (second column is timestamp in seconds starting from zero).
> > 
> > A 0  
> > B 15  
> > C 35  
> > D 100  
> > E 205  
> > F 215  
> > G 300
> > 
> > would result in
> > 
> > [[A, B, C], [D], [E, F], [G]]
> > 
> > Any help on how to do this in the "Ruby Way" would be appreciated.
> 
> --  
> remember.guy do |as, often| as.you\_can - without end  
> [http://blog.rubybestpractices.com/](http://blog.rubybestpractices.com/)

---

<div class="post-metadata">

**Author:** ![Glenn\_Jackman](https://avatars.discourse-cdn.com/v4/letter/g/ce73a5/32.png) [@Glenn\_Jackman](https://rubytalk.org/u/Glenn_Jackman)\
**Post date:** [20 March 2010 12:42 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797/8 "2010-03-20T12:42:13Z")

</div>

Lots of verbose answers. It can be quite short:

&nbsp;&nbsp;&nbsp;&nbsp;a = [['a',0],['b',15],['c',35],['d',100],['e',205],['f',215],['g',300]]

&nbsp;&nbsp;&nbsp;&nbsp;a.group\_by {|name,val| val/100} .  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;collect do |key,list|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.collect {|name, time| name}  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;# =\> [["a", "b", "c"], ["d"], ["e", "f"], ["g"]]

> **···**
>
> At 2010-03-18 06:50PM, "Steve Wilhelm" wrote:
> 
> > I have an array of records that contain timestamps at random intervals.  
> > The records are ordered by timestamp.
> > 
> > I would like to convert the array into an array of arrays; each subarray  
> > would contain "grouped records." Grouping would occur if the timestamp  
> > of the next element in the original array is within thirty seconds of  
> > the current element.
> > 
> > Example (second column is timestamp in seconds starting from zero).
> > 
> > A 0  
> > B 15  
> > C 35  
> > D 100  
> > E 205  
> > F 215  
> > G 300
> > 
> > would result in
> > 
> > [[A, B, C], [D], [E, F], [G]]
> > 
> > Any help on how to do this in the "Ruby Way" would be appreciated.
> 
> --  
> Glenn Jackman  
> &nbsp;&nbsp;&nbsp;&nbsp;Write a wise saying and your name will live forever. -- Anonymous

---

<div class="post-metadata">

**Author:** ![Tanaka\_Akira](https://avatars.discourse-cdn.com/v4/letter/t/439d5e/32.png) [@Tanaka\_Akira](https://rubytalk.org/u/Tanaka_Akira)\
**Post date:** [21 March 2010 00:57 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797/9 "2010-03-21T00:57:02Z")

</div>

I think this kind of problems which slices consecutive elements in an array  
is not well supported in Ruby.

Enumerable#each\_slice is not usable because it slices for each fixed number  
of elements.

Ruby 1.9.2 has Enumerable#slice\_before and it is usable but not so elegant  
because it needs to maintain previous element.

% ruby -e '  
a = [  
&nbsp;&nbsp;["A", 0],  
&nbsp;&nbsp;["B", 15],  
&nbsp;&nbsp;["C", 35],  
&nbsp;&nbsp;["D", 100],  
&nbsp;&nbsp;["E", 205],  
&nbsp;&nbsp;["F", 215],  
&nbsp;&nbsp;["G", 300]  
]  
prev = nil  
p a.slice\_before {|s,t|  
&nbsp;&nbsp;&nbsp;&nbsp;tmp, prev = prev, t  
&nbsp;&nbsp;&nbsp;&nbsp;tmp && (t-tmp) \> 30  
&nbsp;&nbsp;}.map {|es|  
&nbsp;&nbsp;&nbsp;&nbsp;es.map {|s,t| s }  
&nbsp;&nbsp;}  
'  
[["A", "B", "C"], ["D"], ["E", "F"], ["G"]]

We may need Enumerable#slice\_between.

% ruby -e '  
module Enumerable  
&nbsp;&nbsp;def slice\_between(&b)  
&nbsp;&nbsp;&nbsp;&nbsp;Enumerator.new {|y|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;first = true  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buf =   
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;prev = nil  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.each {|elt|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if first  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;first = false  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buf \<\< elt  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;prev = elt  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if b.call(prev, elt)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y \<\< buf  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buf = [elt]  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buf \<\< elt  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;prev = elt  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if !buf.empty?  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y \<\< buf  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;}  
&nbsp;&nbsp;end  
end  
a = [  
&nbsp;&nbsp;["A", 0],  
&nbsp;&nbsp;["B", 15],  
&nbsp;&nbsp;["C", 35],  
&nbsp;&nbsp;["D", 100],  
&nbsp;&nbsp;["E", 205],  
&nbsp;&nbsp;["F", 215],  
&nbsp;&nbsp;["G", 300]  
]  
p a.slice\_between {|(s1,t1),(s2,t2)|  
&nbsp;&nbsp;&nbsp;&nbsp;(t2-t1) \< 30  
&nbsp;&nbsp;}.map {|es|  
&nbsp;&nbsp;&nbsp;&nbsp;es.map {|s,t| s }  
&nbsp;&nbsp;}  
'  
[["A"], ["B"], ["C", "D", "E"], ["F", "G"]]

> **···**
>
> 2010/3/19 Steve Wilhelm \<steve@studio831.com\>:
> 
> > Example (second column is timestamp in seconds starting from zero).
> > 
> > A 0  
> > B 15  
> > C 35  
> > D 100  
> > E 205  
> > F 215  
> > G 300
> > 
> > would result in
> > 
> > [[A, B, C], [D], [E, F], [G]]
> 
> --  
> Tanaka Akira

---

<div class="post-metadata">

**Author:** ![Steve\_Wilhelm](https://avatars.discourse-cdn.com/v4/letter/s/b9bd4f/32.png) [@Steve\_Wilhelm](https://rubytalk.org/u/Steve_Wilhelm)\
**Post date:** [19 March 2010 02:36 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797/10 "2010-03-19T02:36:00Z")

</div>

Josh Cheek wrote:

> **···**
>
> > On Thu, Mar 18, 2010 at 4:50 PM, Steve Wilhelm \<steve@studio831.com\> \> wrote:
> > 
> > > A 0
> > > 
> > > Any help on how to do this in the "Ruby Way" would be appreciated.
> > > 
> > > - Steve W.  
> > > --  
> > > Posted via [http://www.ruby-forum.com/\](http://www.ruby-forum.com/%5C).
> > 
> > What about  
> > A 0  
> > B 20  
> > C 40
> > 
> > Does that become  
> > [[A,B],[B,C]] or [[A,B,C]] or something else? The congruence class here  
> > is  
> > unclear.
> 
> It would be [[A,B,C]].
> 
> - Steve W.  
> --  
> Posted via [http://www.ruby-forum.com/\](http://www.ruby-forum.com/%5C).

---

<div class="post-metadata">

**Author:** ![Urabe\_Shyouhei1](https://avatars.discourse-cdn.com/v4/letter/u/6a8cbe/32.png) [@Urabe\_Shyouhei1](https://rubytalk.org/u/Urabe_Shyouhei1)\
**Post date:** [19 March 2010 03:29 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797/11 "2010-03-19T03:29:23Z")

</div>

Roger Braun wrote:

> How about this:

You should really know about Enumerable#group\_by.

irb(main):001:0\> [0,15,35,100,205,300].group\_by {|i| i/100 }  
=\> {0=\>[0, 15, 35], 1=\>[100], 2=\>[205], 3=\>[300]}

---

<div class="post-metadata">

**Author:** ![Steve\_Wilhelm](https://avatars.discourse-cdn.com/v4/letter/s/b9bd4f/32.png) [@Steve\_Wilhelm](https://rubytalk.org/u/Steve_Wilhelm)\
**Post date:** [19 March 2010 21:07 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797/12 "2010-03-19T21:07:01Z")

</div>

After reviewing the suggestions, here is my code. records is the result  
of a ActiveRecord::find with a member called timestamp containing a UNIX  
timestamp.

I introduced an addition of a default value for the find\_index call and  
included a descending sort of the groups.

&nbsp;&nbsp;edges = records.each\_cons(2).group\_by {|(record\_x, record\_y)|  
record\_y.timestamp - record\_x.timestamp \< 30 }[false].map{ |edge|  
edge[0] }

&nbsp;&nbsp;groups = records.group\_by { |record| edges.find\_index {|edge|  
record.timestamp \<= edge.timestamp } || edges.count }.values.sort\_by {

> e\> -e.count }

Thanks again for everyone's help.

- Steve W.

> **···**
>
> --  
> Posted via [http://www.ruby-forum.com/\](http://www.ruby-forum.com/%5C).

---

<div class="post-metadata">

**Author:** ![Robert\_K1](https://yyz1.discourse-cdn.com/flex029/user_avatar/rubytalk.org/robert_k1/32/1830_2.png) [@Robert\_K1](https://rubytalk.org/u/Robert_K1)\
**Post date:** [20 March 2010 12:42 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797/13 "2010-03-20T12:42:29Z")

</div>

Yeah, but as far as I can see that's not a solution to the problem the OP wanted to solve. He wants to build chains based on the delta and not based on the fact that they fall in the same interval. Am I missing something?

Kind regards

&nbsp;&nbsp;robert

> **···**
>
> On 03/19/2010 03:33 PM, Glenn Jackman wrote:
> 
> > At 2010-03-18 06:50PM, "Steve Wilhelm" wrote:
> > 
> > > I have an array of records that contain timestamps at random intervals.  
> > > The records are ordered by timestamp.  
> > > &nbsp;&nbsp;I would like to convert the array into an array of arrays; each subarray  
> > > would contain "grouped records." Grouping would occur if the timestamp  
> > > of the next element in the original array is within thirty seconds of  
> > > the current element.  
> > > &nbsp;&nbsp;Example (second column is timestamp in seconds starting from zero).  
> > > &nbsp;&nbsp;A 0  
> > > B 15  
> > > C 35  
> > > D 100  
> > > E 205  
> > > F 215  
> > > G 300  
> > > &nbsp;&nbsp;would result in  
> > > &nbsp;&nbsp;[[A, B, C], [D], [E, F], [G]]  
> > > &nbsp;&nbsp;Any help on how to do this in the "Ruby Way" would be appreciated.
> > 
> > Lots of verbose answers. It can be quite short:
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;a = [['a',0],['b',15],['c',35],['d',100],['e',205],['f',215],['g',300]]
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;a.group\_by {|name,val| val/100} .  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;collect do |key,list| list.collect {|name, time| name}  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;# =\> [["a", "b", "c"], ["d"], ["e", "f"], ["g"]]
> 
> --  
> remember.guy do |as, often| as.you\_can - without end  
> [http://blog.rubybestpractices.com/](http://blog.rubybestpractices.com/)

---

<div class="post-metadata">

**Author:** ![Urabe\_Shyouhei1](https://avatars.discourse-cdn.com/v4/letter/u/6a8cbe/32.png) [@Urabe\_Shyouhei1](https://rubytalk.org/u/Urabe_Shyouhei1)\
**Post date:** [21 March 2010 06:42 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797/14 "2010-03-21T06:42:22Z")

</div>

Tanaka Akira wrote:

> I think this kind of problems which slices consecutive elements in an array  
> is not well supported in Ruby.

+1. There seems to be some real applications where that kind of tools are nifty.

---

<div class="post-metadata">

**Author:** ![Colin\_Bartlett1](https://avatars.discourse-cdn.com/v4/letter/c/dbc845/32.png) [@Colin\_Bartlett1](https://rubytalk.org/u/Colin_Bartlett1)\
**Post date:** [21 March 2010 07:23 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797/15 "2010-03-21T07:23:00Z")

</div>

> > I think this kind of problems which slices consecutive elements in an array  
> > is not well supported in Ruby.

> +1. There seems to be some real applications where that kind of tools are nifty.

> > Enumerable#each\_slice is not usable because it slices for each fixed number  
> > of elements.  
> > Ruby 1.9.2 has Enumerable#slice\_before and it is usable but not so elegant  
> > because it needs to maintain previous element.

...

> > We may need Enumerable#slice\_between.

Is a really elegant method possible for this sort of thing?  
Some time ago I was trying to find duplicate files,  
and set up an array of files with elements (paths, sizes, checksums),  
and then wrote an Enumerable#each\_group method.  
The only way I could think of differentiating a yield for "comparison"  
from a yield of the wanted array of "similar" objects  
was to have the first item of each yield to be a boolean  
with true meaning compare, and false meaning it's the wanted array.

I'd be interested in seeing a better Enumerable#each\_group method,  
if one is possible.

In the meantime, below is what I wrote for Enumerable#each\_group,  
with its application to Steve Wilhelm's problem.

module Enumerable  
&nbsp;&nbsp;# Deeming successive enumerable objects to be "equivalent"  
&nbsp;&nbsp;# using a block compare, yields an array of the equivalent items.  
&nbsp;&nbsp;def each\_group\_use\_block\_compare()  
&nbsp;&nbsp;&nbsp;&nbsp;arr = nil ; obj\_g = nil  
&nbsp;&nbsp;&nbsp;&nbsp;self.each do | obj |  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if arr then  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# first item in yield is "true" indicating a yield for comparison  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( yield true, obj\_g, obj ) == 0 then  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;arr \<\< obj  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj\_g = obj # group by adjacent objects, not by first in group  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;next  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# first item in yield is "false" indicating a yield of the group  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;yield false, arr  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj\_g = obj ; arr = [obj]  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;if arr then  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# first item in yield is "false" indicating a yield of the group  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;yield false, arr  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;return self  
&nbsp;&nbsp;end  
end

# for the problem of Steve Wilhelm  
def group\_for\_sw( arr )  
&nbsp;&nbsp;arrg =   
&nbsp;&nbsp;arr.each\_group\_use\_block\_compare do | q\_compare, aa, bb |  
&nbsp;&nbsp;&nbsp;&nbsp;if q\_compare then  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if bb[1] - aa[1] \< 30 then 0 else -1 end  
&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;aa.map! { |cc| cc[0] } # to preserve A, B, C objects, omit this  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;arrg \<\< aa  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end  
&nbsp;&nbsp;arrg  
end

arr = [[ :A, 0], [:B, 15], [:C, 35],  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[:D, 100],  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[:E, 205], [:F, 215],  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[:G, 300]  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]  
arrg = group\_for\_sw( arr )  
p arrg #=\> [[:A, :B, :C], [:D], [:E, :F], [:G]]

> **···**
>
> On Sun, Mar 21, 2010 at 12:57 AM, Tanaka Akira wrote:  
> On Sun, Mar 21, 2010 at 6:42 AM, Urabe Shyouhei wrote:  
> On Sun, Mar 21, 2010 at 12:57 AM, Tanaka Akira also wrote:

---

<div class="post-metadata">

**Author:** ![Glenn\_Jackman](https://avatars.discourse-cdn.com/v4/letter/g/ce73a5/32.png) [@Glenn\_Jackman](https://rubytalk.org/u/Glenn_Jackman)\
**Post date:** [22 March 2010 16:45 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797/16 "2010-03-22T16:45:09Z")

</div>

> \> Example (second column is timestamp in seconds starting from zero).  
> \>  
> \> A 0  
> \> B 15  
> \> C 35  
> \> D 100  
> \> E 205  
> \> F 215  
> \> G 300  
> \>  
> \> would result in  
> \>  
> \> [[A, B, C], [D], [E, F], [G]]
> 
> I think this kind of problems which slices consecutive elements in an array  
> is not well supported in Ruby.
> 
> Enumerable#each\_slice is not usable because it slices for each fixed number  
> of elements.

I guess that's why Enumerable#each\_cons exists, to iterate over a  
collection and look at the next n consecutive elements.

&nbsp;&nbsp;&nbsp;&nbsp;a = [[:A,0],[:B,15],[:C,35],[:D,100],[:E,205],[:F,215],[:G,300]]

&nbsp;&nbsp;&nbsp;&nbsp;all =   
&nbsp;&nbsp;&nbsp;&nbsp;current = [a[0][0]]

&nbsp;&nbsp;&nbsp;&nbsp;a.each\_cons(2) do |m, n|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if n[1] - m[1] \< 30  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;current \<\< n[0]  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;all \<\< current  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;current = [n[0]]  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;all \<\< current  
&nbsp;&nbsp;&nbsp;&nbsp;p all

> **···**
>
> At 2010-03-20 08:57PM, "Tanaka Akira" wrote:
> 
> > 2010/3/19 Steve Wilhelm \<steve@studio831.com\>:
> 
> > Ruby 1.9.2 has Enumerable#slice\_before and it is usable but not so elegant  
> > because it needs to maintain previous element.
> > 
> > % ruby -e '  
> > a = [  
> > &nbsp;&nbsp;&nbsp;["A", 0],  
> > &nbsp;&nbsp;&nbsp;["B", 15],  
> > &nbsp;&nbsp;&nbsp;["C", 35],  
> > &nbsp;&nbsp;&nbsp;["D", 100],  
> > &nbsp;&nbsp;&nbsp;["E", 205],  
> > &nbsp;&nbsp;&nbsp;["F", 215],  
> > &nbsp;&nbsp;&nbsp;["G", 300]  
> > ]  
> > prev = nil  
> > p a.slice\_before {|s,t|  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tmp, prev = prev, t  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tmp && (t-tmp) \> 30  
> > &nbsp;&nbsp;&nbsp;}.map {|es|  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;es.map {|s,t| s }  
> > &nbsp;&nbsp;&nbsp;}  
> > '  
> > [["A", "B", "C"], ["D"], ["E", "F"], ["G"]]
> 
> --  
> Glenn Jackman  
> &nbsp;&nbsp;&nbsp;&nbsp;Write a wise saying and your name will live forever. -- Anonymous

---

<div class="post-metadata">

**Author:** ![Josh\_Cheek](https://avatars.discourse-cdn.com/v4/letter/j/e79b87/32.png) [@Josh\_Cheek](https://rubytalk.org/u/Josh_Cheek)\
**Post date:** [19 March 2010 03:48 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797/17 "2010-03-19T03:48:30Z")

</div>

Your results are correct only because of a happenstance of the data. Add 99  
in there, it should group with 100, but it groups with the 0...100  
congruence class

ruby-1.9.1-p378 \> [0,15,35,99,100,205,300].group\_by {|i| i/100 }  
=\> {0=\>[0, 15, 35, 99], 1=\>[100], 2=\>[205], 3=\>[300]}

Because these groups are relative to each other, I think you must do  
something like Roger or I did, where you iterate through the list and  
compare it to the groups.

> **···**
>
> On Thu, Mar 18, 2010 at 9:29 PM, Urabe Shyouhei \<shyouhei@ruby-lang.org\>wrote:
> 
> > Roger Braun wrote:  
> > \> How about this:
> > 
> > You should really know about Enumerable#group\_by.
> > 
> > irb(main):001:0\> [0,15,35,100,205,300].group\_by {|i| i/100 }  
> > =\> {0=\>[0, 15, 35], 1=\>[100], 2=\>[205], 3=\>[300]}

---

<div class="post-metadata">

**Author:** ![Roger\_Braun](https://avatars.discourse-cdn.com/v4/letter/r/e9bcb4/32.png) [@Roger\_Braun](https://rubytalk.org/u/Roger_Braun)\
**Post date:** [19 March 2010 03:49 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797/18 "2010-03-19T03:49:04Z")

</div>

This does not solve the problem.

irb(main):011:0\> [0,15,35,99,100,205,300].group\_by{|i| i/100}  
=\> {0=\>[0, 15, 35, 99], 1=\>[100], 2=\>[205], 3=\>[300]}

but should be

[[0, 15, 35], [99, 100], [205], [300]]

at least if I understood the problem correctly.

> **···**
>
> On Fri, Mar 19, 2010 at 4:29 AM, Urabe Shyouhei \<shyouhei@ruby-lang.org\> wrote:
> 
> > Roger Braun wrote:
> > 
> > > How about this:
> > 
> > You should really know about Enumerable#group\_by.
> > 
> > irb(main):001:0\> [0,15,35,100,205,300].group\_by {|i| i/100 }  
> > =\> {0=\>[0, 15, 35], 1=\>[100], 2=\>[205], 3=\>[300]}
> 
> --  
> Roger Braun  
> [http://yononaka.de](http://yononaka.de)  
> roger.braun@student.uni-tuebingen.de

---

<div class="post-metadata">

**Author:** ![Glenn\_Jackman](https://avatars.discourse-cdn.com/v4/letter/g/ce73a5/32.png) [@Glenn\_Jackman](https://rubytalk.org/u/Glenn_Jackman)\
**Post date:** [20 March 2010 12:42 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797/19 "2010-03-20T12:42:27Z")

</div>

> \>\> I would like to convert the array into an array of arrays; each subarray  
> \>\> would contain "grouped records." Grouping would occur if the timestamp  
> \>\> of the next element in the original array is within thirty seconds of  
> \>\> the current element.

[...]

> Yeah, but as far as I can see that's not a solution to the problem the  
> OP wanted to solve. He wants to build chains based on the delta and not  
> based on the fact that they fall in the same interval. Am I missing  
> something?

Ah, I didn't read the words that closely -- I was looking at the input  
and output data instead.

> **···**
>
> At 2010-03-19 01:52PM, "Robert Klemme" wrote:
> 
> > On 03/19/2010 03:33 PM, Glenn Jackman wrote:  
> > \> At 2010-03-18 06:50PM, "Steve Wilhelm" wrote:
> 
> --  
> Glenn Jackman  
> &nbsp;&nbsp;&nbsp;&nbsp;Write a wise saying and your name will live forever. -- Anonymous

---

<div class="post-metadata">

**Author:** ![Tanaka\_Akira](https://avatars.discourse-cdn.com/v4/letter/t/439d5e/32.png) [@Tanaka\_Akira](https://rubytalk.org/u/Tanaka_Akira)\
**Post date:** [21 March 2010 07:58 UTC](https://rubytalk.org/t/grouping-elements-of-an-array/57797/20 "2010-03-21T07:58:02Z")

</div>

We need two blocks.  
One for slice condition and one for sliced array.  
But Ruby's method call can take only one block at most.

Enumerable#slice\_before solves this problem by returning enumerator.

enum.slice\_before {|elt| condition }.each {|ary| ... }

slice\_between presented in [ruby-talk:359384] is similar.

enum.slice\_between {|elt1, elt2| condition }.each {|ary| ... }

Another possible idea is using Proc argument.  
enum.foo(lambda {|elt| condition }) {|ary| ... }

> **···**
>
> 2010/3/21 Colin Bartlett \<colinb2r@googlemail.com\>:
> 
> > Is a really elegant method possible for this sort of thing?  
> > Some time ago I was trying to find duplicate files,  
> > and set up an array of files with elements (paths, sizes, checksums),  
> > and then wrote an Enumerable#each\_group method.  
> > The only way I could think of differentiating a yield for "comparison"  
> > from a yield of the wanted array of "similar" objects  
> > was to have the first item of each yield to be a boolean  
> > with true meaning compare, and false meaning it's the wanted array.
> 
> --  
> Tanaka Akira

[Next page](https://rubytalk.org/t/grouping-elements-of-an-array/57797.md?page=2)
