[GOLF]: 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

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 Thu, 7 Dec 2006, Max Muermann wrote:

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

--
if you want others to be happy, practice compassion.
if you want to be happy, practice compassion. -- the dalai lama

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 12/7/06, Max Muermann <ruby@muermann.org> wrote:

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

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

···

On Thu, 07 Dec 2006 03:46:37 -0000, Max Muermann <ruby@muermann.org> wrote:

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

--
Ross Bamford - rosco@roscopeco.remove.co.uk

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

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

···

On 07.12.2006 09:46, Ross Bamford wrote:

On Thu, 07 Dec 2006 03:46:37 -0000, Max Muermann <ruby@muermann.org> wrote:

This is common enough that it deserves to be its own constructor, imo
- Hash.multi perhaps

martin

···

On 12/7/06, Robert Klemme <shortcutter@googlemail.com> wrote:

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

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

···

On 12/7/06, Robert Klemme <shortcutter@googlemail.com> wrote:

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

···

On Thu, 7 Dec 2006, Martin DeMello wrote:

On 12/7/06, Robert Klemme <shortcutter@googlemail.com> wrote:

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

This is common enough that it deserves to be its own constructor, imo
- Hash.multi perhaps

--
if you want others to be happy, practice compassion.
if you want to be happy, practice compassion. -- the dalai lama

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

    Hash.new_by ''

T.

···

ara.t.howard@noaa.gov wrote:

On Thu, 7 Dec 2006, Martin DeMello wrote:

> On 12/7/06, Robert Klemme <shortcutter@googlemail.com> 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