[GOLF]: partitioning an array

M

Max Muermann

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
 
A

ara.t.howard

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

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
 
R

Robert Klemme

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

The last line is definitively superfluous in the light of Hash#values.
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"]]
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
 
D

Dave Burt

Martin said:
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
 
A

ara.t.howard

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
 
T

Trans

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

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

Hash.new_by '[]'

T.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top