Collect_by

For all you FPers out there - is this a defined function in any
language’s standard library? And if so, what is it called?

module Enumerable
def collect_by

h = {}
each {|i|
  j = yield i
  (h[j] ||= []) << i
}
h

# or, as a one-liner
# inject({}) {|c, e| (c[yield e] ||= []) << e; c}

end
end

a = [1,2,3,4,5,6,7,8,9,10]
p a.collect_by {|i| i % 2} #=> {0=>[2, 4, 6, 8, 10], 1=>[1, 3, 5, 7, 9]}

martin

No, it’s not in the standard library. I called it “parition_by”:

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/577

I am sure this method was reinvented several times until now, but named
different each time. :wink:

···

On 2003-07-31 02:52:21 +0900, Martin DeMello wrote:

For all you FPers out there - is this a defined function in any
language’s standard library? And if so, what is it called?


Who controls the past controls the future. Who controls the present
controls the past.
– George Orwell, 1984

Great idea.

I were just experimenting, when I discovered a glitch in
my understanding of Ruby…

What is the difference between ‘Enumerable1’ and ‘Enumerable’.
Why does the ‘Enumerable’ work and the other not ???

Please enligthen me :slight_smile:

module Enumerable1
def collect_by
h = Hash.new []
each{|i| h[yield(i)].push i}
h
end
end

module Enumerable
def collect_by
h = {}
each{|i| (h[yield(i)] ||= []) << i}
h
end
end

a = (1…10).to_a
p a.collect_by {|i| i % 2}

Enumerable1

#=> {}

Enumerable

#=> {0=>[2, 4, 6, 8, 10], 1=>[1, 3, 5, 7, 9]}

···


Simon Strandgaard

Hi –

Great idea.

I were just experimenting, when I discovered a glitch in
my understanding of Ruby…

What is the difference between ‘Enumerable1’ and ‘Enumerable’.
Why does the ‘Enumerable’ work and the other not ???

Please enligthen me :slight_smile:

module Enumerable1
def collect_by
h = Hash.new
each{|i| h[yield(i)].push i}
h
end
end
[…]

Enumerable1

#=> {}

What you’re seeing is a result of the fact that fetching a default
value for a hash does not actually set the key for the hash –
because, by definition, the default value is what you get when you use
a non-existent key, and it doesn’t imply assignment. For example:

irb(main):001:0> h = Hash.new(“hi”)
{}
irb(main):002:0> h[1]
“hi”
irb(main):003:0> h
{}

(Another issue is that the way you’ve written it, h[…] will always
return the same array. But that’s another matter :slight_smile: You can
circumvent that, in 1.8, with: h = Hash.new { } )

David

···

On Thu, 31 Jul 2003, Simon Strandgaard wrote:


David Alan Black
home: dblack@superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

Yes, my very favorite 1.8 line:

h = Hash.new { |hash,key| hash[key] = }

···

On Jul 31, dblack@superlink.net wrote:

Hi –

On Thu, 31 Jul 2003, Simon Strandgaard wrote:

Great idea.

I were just experimenting, when I discovered a glitch in
my understanding of Ruby…

What is the difference between ‘Enumerable1’ and ‘Enumerable’.
Why does the ‘Enumerable’ work and the other not ???

Please enligthen me :slight_smile:

module Enumerable1
def collect_by
h = Hash.new
each{|i| h[yield(i)].push i}
h
end
end
[…]

Enumerable1

#=> {}

What you’re seeing is a result of the fact that fetching a default
value for a hash does not actually set the key for the hash –
because, by definition, the default value is what you get when you use
a non-existent key, and it doesn’t imply assignment. For example:

irb(main):001:0> h = Hash.new(“hi”)
{}
irb(main):002:0> h[1]
“hi”
irb(main):003:0> h
{}

(Another issue is that the way you’ve written it, h[…] will always
return the same array. But that’s another matter :slight_smile: You can
circumvent that, in 1.8, with: h = Hash.new { } )

Please enligthen me :slight_smile:
[snip]
(Another issue is that the way you’ve written it, h[…] will always
return the same array. But that’s another matter :slight_smile: You can
circumvent that, in 1.8, with: h = Hash.new { } )

Oh… I luckily forgot about this. Still it won’t work…
What am I doing wrong ?

module Enumerable
def collect_by
h = Hash.new { Array.new } # braces :slight_smile:
each{|i| h[yield(i)].push i}
h
end
end

a = (1…10).to_a
p a.collect_by {|i| i % 2}
#=> {}

···

On Thu, 31 Jul 2003 08:17:44 +0900, dblac wrote:

On Thu, 31 Jul 2003, Simon Strandgaard wrote:


Simon Strandgaard

Hi –

···

On Thu, 31 Jul 2003, Simon Strandgaard wrote:

On Thu, 31 Jul 2003 08:17:44 +0900, dblac wrote:

On Thu, 31 Jul 2003, Simon Strandgaard wrote:

Please enligthen me :slight_smile:
[snip]
(Another issue is that the way you’ve written it, h[…] will always
return the same array. But that’s another matter :slight_smile: You can
circumvent that, in 1.8, with: h = Hash.new { } )

Oh… I luckily forgot about this. Still it won’t work…
What am I doing wrong ?

module Enumerable
def collect_by
h = Hash.new { Array.new } # braces :slight_smile:
each{|i| h[yield(i)].push i}
h
end
end

a = (1…10).to_a
p a.collect_by {|i| i % 2}
#=> {}

Like I said, that’s just a side issue :slight_smile: You’re still doing the same
thing: not assigning anything to any keys in the hash. You’re just
retrieving the default value, altering it, and throwing it away.

You could use what Brett suggested, to actually do the assignments:

h = Hash.new {|hash,key| hash[key] = }

David


David Alan Black
home: dblack@superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

  1. h[foo] returns a new empty array
  2. you push something onto that array
  3. no reference to that array is stored anywhere
  4. the array disappears into the ether, to get garbage-collected at
    a later date

The important thing is, step (1) does not assign anything to the hash.
h remains as {} throughout.

Regards,

Brian.

···

On Thu, Jul 31, 2003 at 08:14:59AM +0900, Simon Strandgaard wrote:

On Thu, 31 Jul 2003 08:17:44 +0900, dblac wrote:

On Thu, 31 Jul 2003, Simon Strandgaard wrote:

Please enligthen me :slight_smile:
[snip]
(Another issue is that the way you’ve written it, h[…] will always
return the same array. But that’s another matter :slight_smile: You can
circumvent that, in 1.8, with: h = Hash.new { } )

Oh… I luckily forgot about this. Still it won’t work…
What am I doing wrong ?

module Enumerable
def collect_by
h = Hash.new { Array.new } # braces :slight_smile:
each{|i| h[yield(i)].push i}
h
end
end

a = (1…10).to_a
p a.collect_by {|i| i % 2}
#=> {}