: partitioning an array

Take an array of objects and partition it into subarrays, based on
some arbitrary property of the objects. I would use this for example
for generating a report of purchase orders and grouped by week, month,
year, approximate value, etc.

The idea is similar to Array#partition, but where partition only does
a true/false check, the resulting array should be partitioned by the
return value of a block so that:

a = [‘a’,‘bc’,‘def’,‘g’,‘hi’,‘jkl’,‘m’]
group(a) {|i| i.size} #=> [[“a”, “g”, “m”], [“bc”, “hi”], [“def”,
“jkl”]]

My best effort so far is this:

def group array, &block
h = {}
array.each do |e|
(h[yield(e)]||=[])<<e
end
h.to_a.map {|e| e[1] }
end

For some reason, I cannot bring myself to like this. I have the
nagging feeling that there is a more elegant way…

Cheers,
Max

On Thu, 7 Dec 2006, Max M. wrote:

group(a) {|i| i.size} #=> [[“a”, “g”, “m”], [“bc”, “hi”], [“def”, “jkl”]]
end

For some reason, I cannot bring myself to like this. I have the
nagging feeling that there is a more elegant way…

Cheers,
Max

it’s not a good golf solution, but here’s a slightly differnet approach:

harp:~ > cat a.rb
module Enumerable
def group_by &b
h = Hash.new{|h,k| h[k] = []}
each{|x| h[x.instance_eval(&b)] << x}
h.values
end
end

a = %w[ a bc def g hi jkl m ]
p a.group_by{ size }

h = { ‘k’ => ‘v’, ‘K’ => ‘V’, ‘a’ => ‘b’, ‘A’ => ‘b’ }
p h.group_by{ first.downcase }

harp:~ > ruby a.rb
[[“a”, “g”, “m”], [“bc”, “hi”], [“def”, “jkl”]]
[[[“K”, “V”], [“k”, “v”]], [[“A”, “b”], [“a”, “b”]]]

regards.

-a

On 12/7/06, Max M. [email protected] wrote:

a = [‘a’,‘bc’,‘def’,‘g’,‘hi’,‘jkl’,‘m’]
h.to_a.map {|e| e[1] }
end

def group a
a.inject({}){|h,v|(h[yield(v)]||=[])<<v;h}.values
end

slightly more compact, but not very original. I copied from you and
ara.
Doesn’t look too noice either.

On Thu, 07 Dec 2006 03:46:37 -0000, Max M. [email protected]
wrote:

group(a) {|i| i.size} #=> [[“a”, “g”, “m”], [“bc”, “hi”], [“def”, “jkl”]]
end

For some reason, I cannot bring myself to like this. I have the
nagging feeling that there is a more elegant way…

Maybe:

a = [‘a’,‘bc’,‘def’,‘g’,‘hi’,‘jkl’,‘m’]

=> [“a”, “bc”, “def”, “g”, “hi”, “jkl”, “m”]

a.inject([]){|dst,e|(dst[e.length-1]||=[])<<e;dst}

=> [[“a”, “g”, “m”], [“bc”, “hi”], [“def”, “jkl”]]

a.inject({}){|h,v|(h[yield(v)]||=[])<<v;h}.values

It’s actually cleaner and shorter without that inject.

def group a
h={}
a.map{|v|(h[yield(v)]||=[])<<v}
h.values
end

and if we’re golfing here, it’s (slightly) shorter to add that extra arg
and dump that yield:

def group a;h={};a.map{|v|(h[yield(v)]||=[])<<v};h.values;end
def group a,&b;h={};a.map{|v|(h[b[v]]||=[])<<v};h.values;end

On 07.12.2006 09:46, Ross B. wrote:

end
h.to_a.map {|e| e[1] }

The last line is definitively superfluous in the light of Hash#values.

a.inject([]){|dst,e|(dst[e.length-1]||=[])<<e;dst}

=> [[“a”, “g”, “m”], [“bc”, “hi”], [“def”, “jkl”]]

But:

%w{a bbb}.inject([]){|dst,e|(dst[e.length-1]||=[])<<e;dst}
=> [[“a”], nil, [“bbb”]]

I did

a = [‘a’,‘bc’,‘def’,‘g’,‘hi’,‘jkl’,‘m’]
=> [“a”, “bc”, “def”, “g”, “hi”, “jkl”, “m”]

a.inject(Hash.new {|h,k| h[k]=[]}) {|r,x| r[x.size] << x; r}.values
=> [[“a”, “g”, “m”], [“bc”, “hi”], [“def”, “jkl”]]

%w{a bbb}.inject(Hash.new {|h,k| h[k]=[]}) {|r,x| r[x.size] << x;
r}.values
=> [[“a”], [“bbb”]]

Whether that’s nicer - I don’t know.

Kind regards

robert

On 12/7/06, Robert K. [email protected] wrote:

Hash.new {|h,k| h[k]=[]}

This is common enough that it deserves to be its own constructor, imo

  • Hash.multi perhaps

martin

Martin DeMello wrote:

On 12/7/06, Robert K. [email protected] wrote:

Hash.new {|h,k| h[k]=[]}

This is common enough that it deserves to be its own constructor, imo

  • Hash.multi perhaps

Would you want one of these, too?

class Hash
def self.new_nested
f = proc {|h, k| h[k] = new(&f) }
new(&f)
end
end

Cheers,
Dave

[email protected] wrote:

def Hash.list list_class = Array, *a, &b
Hash.new {|h,k| h[k] = list_class.new(*a, &b)}
end

def Hash.new_by(o='[]')
  Hash.new {|h,k| h[k] = eval o}
end

Hash.new_by '[]'

T.

On Thu, 7 Dec 2006, Martin DeMello wrote:

On 12/7/06, Robert K. [email protected] wrote:

Hash.new {|h,k| h[k]=[]}

This is common enough that it deserves to be its own constructor, imo

  • Hash.multi perhaps

my own lib has

def Hash.list list_class = Array, *a, &b
Hash.new {|h,k| h[k] = list_class.new(*a, &b)}
end

-a