Grouping/Sorting an Array of objects

Suppose I have an array of two different kinds of objects, each with
different attributes, but both having a date attribute. I'd like to be
able to loop through the array in such a manner as to be able print a
result similar to the following:

07/19/08
   Foo-1 - Name
     attr 1
     attr 2
     [..]

   Foo-2 - Name
     attr 1
     attr 2
     [..]

   Bar - Name
     attr 1
     attr 2
     [..]
07/20/08
  Foo-1 - Name
     attr 1
     attr 2
     [..]

  Bar - Name
     attr 1
     attr 2
     [..]
etc.

showing all of the Foo's first, then the Bar's, for each date.
If I only have Foo's, I can easily do:

@data.sort{|x,y| Time.parse(x.date) <=> Time.parse(y.date)}).each do

foo>

  puts Foo.date # Still shows date on each loop, but okay
  puts Foo.name
  [..]
end

To get things in date order, but when I consider mixing a second object
into the array, that confounds matters for me. I suspect group_by{|item|
item.class} may be part of the solution, but I'm not sure how?

Any pointers would be most appreciated.

Thanks,
Howard

···

--
Posted via http://www.ruby-forum.com/\.

#...
# @data.sort{|x,y| Time.parse(x.date) <=> Time.parse(y.date)}).each do
# |foo|
# puts Foo.date # Still shows date on each loop, but okay
# puts Foo.name
# [..]
# end

try,

  @data.sort_by{|rec| [rec.date, rec.class, rec.name]}

or something like that.

it would be nice if you can post some sample data so we can test it immediately. i'm a slow typist :wink:

# To get things in date order, but when I consider mixing a
# second object into the array, that confounds matters for me.
# I suspect group_by{|item| item.class} may be part of the solution,
# but I'm not sure how?

that is possible too, but let us take it one at a time :wink:

kind regards -botp

···

From: Howard Roberts [mailto:howardroberts@comcast.net]

Hi --

Suppose I have an array of two different kinds of objects, each with
different attributes, but both having a date attribute. I'd like to be
able to loop through the array in such a manner as to be able print a
result similar to the following:

07/19/08
  Foo-1 - Name
    attr 1
    attr 2
    [..]

  Foo-2 - Name
    attr 1
    attr 2
    [..]

  Bar - Name
    attr 1
    attr 2
    [..]
07/20/08
Foo-1 - Name
    attr 1
    attr 2
    [..]

Bar - Name
    attr 1
    attr 2
    [..]
etc.

showing all of the Foo's first, then the Bar's, for each date.
If I only have Foo's, I can easily do:

@data.sort{|x,y| Time.parse(x.date) <=> Time.parse(y.date)}).each do
>foo>
puts Foo.date # Still shows date on each loop, but okay
puts Foo.name
[..]
end

To get things in date order, but when I consider mixing a second object
into the array, that confounds matters for me. I suspect group_by{|item|
item.class} may be part of the solution, but I'm not sure how?

Any pointers would be most appreciated.

Here's a stab at it: http://pastie.org/238521\. I've put the sorting
intelligence in the objects, and also given them to_s methods so you
don't have to do all that formatting in the calling code (though you
can, of course, if you want a different output).

The last ten lines still seem a bit wordy to me but so be it; I have
to go get ready for work :slight_smile:

David

···

On Mon, 21 Jul 2008, Howard Roberts wrote:

--
Rails training from David A. Black and Ruby Power and Light:
     Intro to Ruby on Rails July 21-24 Edison, NJ
  * Advancing With Rails August 18-21 Edison, NJ
  * Co-taught by D.A. Black and Erik Kastner
See http://www.rubypal.com for details and updates!

a.group_by do |o|
  o.date
end.sort.each do |date, list|
  p date

  list.group_by do |o|
    o.class
  end.sort_by do |klass, _|
    [Foo, Bar].index(klass) # kind_of?
  end.each do |klass, list|
    list.each do |o|
      p o
    end
  end
end

gegroet,
Erik V. - http://www.erikveen.dds.nl/

Peña, Botp wrote:

try,

  @data.sort_by{|rec| [rec.date, rec.class, rec.name]}

or something like that.

Something like that is probably what I need, because the app doesn't
like that line for me.

it would be nice if you can post some sample data so we can test it
immediately. i'm a slow typist :wink:

some sample data and pseudo code is here:
http://ruby.pastebin.com/m24b5e03

The output I'm trying to achieve would look like the following, listing
first the date, then the information all of the first objects , and then
all of the second objects for that date:

7/18/08

9:00 AM - Orientation
Location: Conference Room
Welcome, review agenda, and brief Q&A

7:30 AM - First Event
The Smiths invite you for a casual breakfast at Grumpy's

7/19/08
8:00 AM - Workshop I
Location: Room 106
Boring, all day presentation by Dr. Jones

7/20/08
6:00 PM - Second Event
The Jones invite you for a Sociable Dinner at La Nepolara's

7/21/08
8:00 AM - Workshop II
Location: Room 108
Yet another boring, all day presentation by Dr. Jones

2:30 PM - Wrap-Up
Location: Room 108
Summary, Q&A, and lots of free goodies

7:30 AM - Third Event
The Smiths invite you for a casual Breakfast at Mel's

1:00 PM - Fourth Event
Dr. Jones and Staff invite you for a Business Lunch at The Krusty Krab

kind regards -botp

Thank you kindly for replying and a for any suggestions you might offer.

Howard

···

--
Posted via http://www.ruby-forum.com/\.

# some sample data and pseudo code is here:
# http://ruby.pastebin.com/m24b5e03

···

From: Howard Roberts [mailto:howardroberts@comcast.net]
#
# The output I'm trying to achieve would look like the
# following, listing
# first the date, then the information all of the first objects
# , and then
# all of the second objects for that date:
#
# 7/18/08
#
# 9:00 AM - Orientation
# Location: Conference Room
# Welcome, review agenda, and brief Q&A
#
# 7:30 AM - First Event
# The Smiths invite you for a casual breakfast at Grumpy's
#
# 7/19/08
# 8:00 AM - Workshop I
# Location: Room 106
# Boring, all day presentation by Dr. Jones
#....

ok, that is much clearer and complete.

i just modified your last code loop as,

#---code
prev_date=nil
@data.sort_by do |item|
    t,ampm = item.time.split
    hr,min = t.split ":"
    hr = "0"+hr if hr.size==1
    timenew = ampm+hr+min
    [item.date, timenew, item.class.to_s]
  end.each do |item|
  puts "#{item.date.strftime('%m/%d/%y')}\n\n" if prev_date != item.date
  
  if item.is_a? Meeting
    puts "#{item.time} - #{item.activity}\n"
    puts "Location: #{item.location}\n"
    puts "#{item.description}\n\n"
  else
    puts "#{item.time} - #{item.name}\n"
    puts "#{item.attendees} invite you for a #{item.mood} #{item.activity} at #{item.location}\n\n"
  end
  prev_date = item.date
end
#---code

There's some clobbering of time in there since i arranged by time too :slight_smile:

So the output is,

C:\family\ruby>ruby test.rb
07/18/08

7:30 AM - First event
The Smiths invite you for a casual Breakfast at Grumpy's

9:00 AM - Orientation
Location: Conference Room
Welcome, review agenda, and brief Q&A

07/19/08

8:00 AM - Workshop I
Location: Room 106
Boring, all day presentation by Dr. Jones

07/20/08

6:00 PM - Second event
The Jones invite you for a Sociable Dinner at La Nepolara's

07/21/08

7:30 AM - Third event
the Smiths invite you for a casual Breakfast at Mel's

8:00 AM - Workshop II
Location: Room 108
Yet another boring, all day presentation by Dr. Jones

1:00 PM - Third event
Dr. Jones and Staff invite you for a Business Lunch at The Krusty Krab

2:30 PM - Wrap-Up
Location: Room 108
Summary, Q&A, and lots of free goodies

C:\family\ruby>

Is that ok?
Kind regards -botp

Peña, Botp wrote:

prev_date=nil
@data.sort_by do |item|
    t,ampm = item.time.split
    hr,min = t.split ":"
    hr = "0"+hr if hr.size==1
    timenew = ampm+hr+min
    [item.date, timenew, item.class.to_s]
  end.each do |item|

   Brilliant!

There's some clobbering of time in there since i arranged by time too :slight_smile:

   I can adapt my app for that.

Is that ok?
Kind regards -botp

-botp,
This is superb! I was reluctant to ask for help on this at all because
it sounded so much like I was asking "write my code for me." But I would
have never gotten that, because I did not know you could use sort_by in
such a way-- passing an array as the last line of the block. It is
exactly what I needed and I am most grateful-- for the clearer
understanding (and the code :slight_smile:

Thank You,
Howard

···

--
Posted via http://www.ruby-forum.com/\.