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