Display only when date changes

I'm trying to code a table listing appointments. I just want to display a particular date in a table row once, then list all the appointments for that day. The display tomorrow's date in a row once and then list all the appointments for that day, and so forth.

I have the following code, but it's not working. Seems like the new_date = 1 statement doesn't work. What am I doing wrong?

<table id="table_appointments"   <tr>     <th>Client</th>     <th>Service</th>     <th>When</th>     <th></th>     <th></th>     <th></th>   </tr>

<% new_date = 0 %> <% current_date = Date.today %>

<% @appointments.each do |appointment| %>

  <% if new_date == 0 %>     <tr><th colspan="6"> <%= new_date %> | <%= current_date %> | <%= appointment.start.strftime("%Y-%m-%d") %></th></tr>   <% end %>

  <% if current_date == appointment.start.strftime("%Y-%m-%d") %>     <% new_date = 1 %>   <% else %>     <% new_date = 0 %>   <% end %>

  <tr class="<%= cycle('list_line_odd', 'list_line_even') %>">     <td><%= appointment.client.name %></td>     <td><%= appointment.service.name %></td>     <td><%= appointment.start.strftime("%A %b %d %Y at %I:%M %p") %></td>     <td><%= link_to 'Show', appointment %></td>     <td><%= link_to 'Edit', edit_appointment_path(appointment) %></td>     <td><%= link_to 'Destroy', appointment, :confirm => 'Are you sure?', :method => :delete %></td>   </tr> <% end %> </table>

I'm trying to code a table listing appointments. I just want to display a particular date in a table row once, then list all the appointments for that day. The display tomorrow's date in a row once and then list all the appointments for that day, and so forth.

I have the following code, but it's not working. Seems like the new_date = 1 statement doesn't work. What am I doing wrong?

<table id="table_appointments" <tr> <th>Client</th> <th>Service</th> <th>When</th> <th></th> <th></th> <th></th> </tr>

<% new_date = 0 %> <% current_date = Date.today %>

<% @appointments.each do |appointment| %>

<% if new_date == 0 %> <tr><th colspan="6"> <%= new_date %> | <%= current_date %> | <%= appointment.start.strftime("%Y-%m-%d") %></th></tr> <% end %>

<% if current_date == appointment.start.strftime("%Y-%m-%d") %>

The above does not work. current_date is a Date, strftime returns a String, which will never be equal to a Date.

Colin

<% current_date = Date.today %>

<% if current_date == appointment.start.strftime("%Y-%m-%d") %>

Aside from comparing Date with String, which is *never* going to return true...

<% @appointments.each do |appointment| %>

Why not use the .group_by to sort all the appointments by startdate, and then iterate each element of the ordered hash that sort_by returns?

<% @appointments.group_by{ |appointment| appointment.start.strftime("%Y-%m-%d") }.each_pair do |start_date, appointments> %>

That way you remove the old imperative-style temporary "new_date" variables.

I think there are a couple of problems with that, firstly I think there is something missing as you have not specified which field to group by. Secondly the OP does not say but I imagine that the the start time contains a time of day value so it will be necessary to group just on the day part of start.

Colin

<% @appointments.group_by{ |appointment| appointment.start.strftime("%Y-%m-%d") }.each_pair do |start_date, appointments> %>

I think there are a couple of problems with that

nope... just ran it here and it worked fine (which is lucky, because I posted the code without testing it :wink:

there is something missing as you have not specified which field to group by.

Yes I have. The block after group_by takes each appointment's start time and formats it according to the OP's wish, and the result of that is the "field" to group by (this is the Rails function for grouping enumerables, not the AR/SQL grouping functionality...)

Secondly the OP does not say but I imagine that the the start time contains a time of day value so it will be necessary to group just on the day part of start.

That's what the "strftime" does... it changes each "start" field to "yyyy-mm-dd" string.

FWIW, here's the same thing on an "Appointments" table of mine:

Appointment.all.size

=> 87

Appointment.all.group_by {|appointment| appointment.start.strftime("%Y%m%d")}.size

=> 54

Appointment.all.group_by {|appointment| appointment.start.strftime("%Y%m%d")}.first

=> ["20100225", [#<Appointment id: 1, owner_id: 1, regarding_id: 193, creator_id: 1, start: "2010-02-25 19:15:00", end: nil, duration_minutes: 40, subject: "", body: "", reminder: false, reminder_minutes_before_start: 240, appointment_recurrance_id: nil, completed: true, created_at: "2010-02-23 14:10:53", updated_at: "2010-03-04 14:40:48", type: nil>, #<Appointment id: 2, owner_id: 1, regarding_id: 196, creator_id: 1, start: "2010-02-25 19:00:00", end: nil, duration_minutes: nil, subject: "", body: "", reminder: false, reminder_minutes_before_start: nil, appointment_recurrance_id: nil, completed: true, created_at: "2010-02-24 15:29:48", updated_at: "2010-03-13 14:10:41", type: nil>]]

The result of the group_by is an ordered hash, (ostensibly an array of arrays with some Rails magic), with the first value of each element the "group_by" key - in this case 20102225, and the second element an array of the appointments that matched that key.

Yes, of course you are right. As usual I did not look closely enough at what was written.

Sorry

Colin

No worries. Hopefully it all helps the OP.

(Leonel.... are you there....? :slight_smile:

I'm sorry, I usually try to stay away from the computer during weekends haha

I tried the above but I get an error... <% @appointments.group_by { |appointment| appointment.start.strftime("%Y-%m-%d") }.each_pair do |start_date, appointments| %>

  <tr class="<%= cycle('list_line_odd', 'list_line_even') %>">     <td><%= appointment.client.name %></td>     <td><%= appointment.service.name %></td>     <td><%= appointment.start.strftime("%A %b %d %Y at %I:%M %p") %></td>     <td><%= link_to 'Show', appointment %></td>     <td><%= link_to 'Edit', edit_appointment_path(appointment) %></td>     <td><%= link_to 'Destroy', appointment, :confirm => 'Are you sure?', :method => :delete %></td>   </tr> <% end %>

ERROR compile error .../app/views/appointments/index.html.erb:15: syntax error, unexpected '|', expecting '}' { |appointment| appointment.start...    ^ .../app/views/appointments/index.html.erb:15: syntax error, unexpected '}', expecting kEND ...t.start.strftime("%Y-%m-%d") }.each_pair do |start_date, app...

I want to understand what's going on, the each_pair, I'm a bit confused. In my controller, I have this:   def index     @appointments = Appointment.all(:order => 'start', :conditions => [ "start >= ?", Date.today ] )

So it's supposed to already have the appointments ordered by start datetime.

What I'm trying to accomplish is a table like this...

I'm sorry, I usually try to stay away from the computer during weekends haha

I tried the above but I get an error... <% @appointments.group_by { |appointment| appointment.start.strftime("%Y-%m-%d") }.each_pair do |start_date, appointments| %>

<tr class="<%= cycle('list_line_odd', 'list_line_even') %>"> <td><%= appointment.client.name %></td> <td><%= appointment.service.name %></td> <td><%= appointment.start.strftime("%A %b %d %Y at %I:%M %p") %></td> <td><%= link_to 'Show', appointment %></td> <td><%= link_to 'Edit', edit_appointment_path(appointment) %></td> <td><%= link_to 'Destroy', appointment, :confirm => 'Are you sure?', :method => :delete %></td> </tr> <% end %>

ERROR compile error .../app/views/appointments/index.html.erb:15: syntax error, unexpected '|', expecting '}' { |appointment| appointment.start...

Have you got a new line before the { ? It must all be on one line so that ruby knows the { is the start of a block for the group_by call. So the following all on one line:

<% @appointments.group_by { |appointment| appointment.start.strftime("%Y-%m-%d") }.each_pair do |start_date, appointments> %>

Colin

Have you got a new line before the { ? It must all be on one line so that ruby knows the { is the start of a block for the group_by call. So the following all on one line:

<% @appointments.group_by { |appointment| appointment.start.strftime("%Y-%m-%d") }.each_pair do |start_date, appointments> %>

It's now all in one line

This is all too confusing!! How can I understand what's going on? I already studied TWO rails books and seems like I barely touched the minimum basics! I studied Simply Rails by Sitepoint and Agile Web Development with Rails by Pragmatic Programmers. I just ordered the pickaxe book last week and it's on its way, I hope it'll help me understand because nothing makes sense to me right now.

Please help :frowning:

ERROR

------------------------------------------------ undefined local variable or method `appointment' for #<#<Class:0x104c62280>:0x104c60520> Extracted source (around line #16):

13: <% @appointments.group_by { |appointment| appointment.start.strftime("%Y-%d-%d") }.each_pair do |start_date, appointments> %> 14: 15: <tr class="<%= cycle('list_line_odd', 'list_line_even') %>"> 16: <td><%= appointment.client.name %></td> 17: <td><%= appointment.service.name %></td> 18: <td><%= appointment.start.strftime("%A %b %d %Y at %I:%M %p") %></td> 19: <td><%= link_to 'Show', appointment %></td>

Now, I'm definitely not very good with Ruby, let alone Rails, so this is just guesstimation. Take it as you will.

The error looks correct, as in the 'appointment' variable is out of scope where you're using it. IIRC, the scope ends at the closing bracket to the block, so when you call .each_pair, 'appointment' is gone.

Do you wanting to do something with each of the elements in @appointments ?

-J

Now, I'm definitely not very good with Ruby, let alone Rails, so this is just guesstimation. Take it as you will.

The error looks correct, as in the 'appointment' variable is out of scope where you're using it. IIRC, the scope ends at the closing bracket to the block, so when you call .each_pair, 'appointment' is gone.

Do you wanting to do something with each of the elements in @appointments ?

My purpose is to create a table where there is a TR for every date, and then list items for that date. Instead of having a cell in every row repeating the same date. Also, the table is supposed to have more columns, the following example is simpler. Like this...

Please help :frowning:

Give us a chance! We have paying-work to do, you know! :slight_smile:

Right... first things first - your error:

------------------------------------------------ undefined local variable or method `appointment' for #<#<Class:0x104c62280>:0x104c60520> Extracted source (around line #16):

13: <% @appointments.group_by { |appointment| appointment.start.strftime("%Y-%d-%d") }.each_pair do |start_date, appointments> %> 14: 15: <tr class="<%= cycle('list_line_odd', 'list_line_even') %>"> 16: <td><%= appointment.client.name %></td> 17: <td><%= appointment.service.name %></td> 18: <td><%= appointment.start.strftime("%A %b %d %Y at %I:%M %p") %></td> 19: <td><%= link_to 'Show', appointment %></td>

One of the lovelies of Rails is how helpful the error messages are. You're being told that on line 16, you use a variable which is not defined - and it even tells you its name: "appointment". The "each_pair" method is returning "start_date" and "appointments" (and "appointments" is an Array of Appointment objects... so you need to iterate that to draw the table row). Add an extra loop before you render...

<% @appointments.group_by { |appointment| appointment.start.strftime("%Y-%d-%d") }.each_pair do |start_date, appointments> %>   <% appointments.each do |appointment| %>     <tr class="<%= cycle('list_line_odd', 'list_line_even') %>">       <td><%= appointment.client.name %></td>       etc...   <% end %> <% end %>

...it would probably be "better" to pass the appointments array to a partial, and have that render the collection... but that's a separate issue - one step at a time :slight_smile:

This is all too confusing!! How can I understand what's going on?

What don't you understand? The "@appointments.group_by.blah.blah" line? That's just a load of Ruby and Rails methods chained together... you could easily check out each one in the API and read about what they do... but one of the ways I like to find out what stuff like this is doing is play with it in the console. Break it apart and run it one method at a time; see what it returns, and make sure you understand why (with reference to the API docs, and your books)

The separate commands are

@appointments # a collection of appointment objects

group_by { |appointment| appointment.start.strftime("%Y-%d-%d") } # taking the @appointments collection and grouping it by the result of evaluating each appointment's start time as a string as "yyyy-mm-dd". The group_by method returns an ordered hash - see http://api.rubyonrails.org/classes/Enumerable.html#method-i-group_by

each_pair do |start_date, appointments| # "each_pair" is just like "each" but returns the key and value of a hash - so you have access to the "key" (the date that appointments match) and the value (the set of appointments from @appointments that have the same date as the key). http://apidock.com/ruby/Hash/each_pair

I studied Simply Rails by Sitepoint and Agile Web Development with Rails by Pragmatic Programmers. I just ordered the pickaxe book last week and it's on its way

You can't have too many books :slight_smile:

I hope it'll help me understand because nothing makes sense to me right now.

Don't despair - it'll click in the end. I find that browsing the API docs *really* helps me get a grip on the methods - just read through bits when you have free time (man, I know how to party, don't I? :slight_smile: There does seem like there's a lot to get to grips with when you're starting, but it does get easier, and at the end of the day, if you write some code now, that may be a bit clunky, but works - when you find methods that would make it work better/easier, you can always refactor.

The bit @appointments.group_by { |appointment| appointment.start.strftime("%Y-%d-%d") } builds a hash where the key part of each entry in the hash is the date and the value part is an array of appointments for that date, then .each_pair do |start_date, appointments| starts a block with start_date containing the date and appointments containing the appointments for that date. so you want then to say something like <% appointments.each do |appointment| %> to iterate through the appointments for that date. You are getting the error below because appointment is not defined.

I have to say that this is quite a complex bit of code for a beginner to get to grips with, but worth the effort to understand.

Colin

<% @appointments.group_by { |appointment| appointment.start.strftime("%Y-%d-%d") }.each_pair do |start_date, appointments> %>   <% appointments.each do |appointment| %>     <tr class="<%= cycle('list_line_odd', 'list_line_even') %>">       <td><%= appointment.client.name %></td>       etc...   <% end %> <% end %>

Ok I got that, it returns apparently the same. But since it's now in a hash I will try to iterate through the hash. Will try to play with it on the console like you say.

...it would probably be "better" to pass the appointments array to a partial, and have that render the collection... but that's a separate issue - one step at a time :slight_smile:

Will try

What don't you understand? The "@appointments.group_by.blah.blah" line? That's just a load of Ruby and Rails methods chained together... you could easily check out each one in the API and read about what they do... but one of the ways I like to find out what stuff like this is doing is play with it in the console. Break it apart and run it one method at a time; see what it returns, and make sure you understand why (with reference to the API docs, and your books)

Currently, I hate the Rails and Ruby API's.

I program on PHP. Whenever I needed to use some function I wasn't used to using, everything would be easily explained in the documentation. I looove the PHP documentation and the examples. You don't have to interpret anything because it is stated with simplicity.

Now, the Rails and the Ruby API's, it's like I'm trying to read Greek! I read the documentation, I think about it, I try to read it again, nothing, NOTHING is readable. I kno, I kno, might take a while to get used to. Still a newbie :-S

LOL thanks guys! it works now :smiley:

Now that I see it working I understand it better.

Here's the code...

<% @appointments.group_by { |appointment| appointment.start.strftime("%A %b %d") }.each_pair do |start_date, appointments| %>   <tr><th colspan="6" class="appointments_table_row_date"><%= start_date %></th></tr>   <% appointments.each do |appointment| %>     <tr class="<%= cycle('list_line_odd', 'list_line_even') %>">       <td><%= appointment.client.name %></td>       <td><%= appointment.service.name %></td>       <td><%= appointment.start.strftime("%I:%M %p") %></td>       <td><%= link_to 'Show', appointment %></td>       <td><%= link_to 'Edit', edit_appointment_path(appointment) %></td>       <td><%= link_to 'Destroy', appointment, :confirm => 'Are you sure?', :method => :delete %></td>     </tr>   <% end %> <% end %>

Leonel *.* wrote in post #949629: [...]

Currently, I hate the Rails and Ruby API's.

I program on PHP. Whenever I needed to use some function I wasn't used to using, everything would be easily explained in the documentation. I looove the PHP documentation and the examples. You don't have to interpret anything because it is stated with simplicity.

What PHP frameworks have you used? How much object-oriented programming have you done? If the answer to either of these questions is "none", you're probably not used to working with big libraries, and so you probably don't yet know how to read docs effectively. (That's not meant as an insult.)

The Rails API documentation is very clearly written in most places. You *do* need to understand the Ruby language and the basics of the Rails framework (as given in the Guides), though.

Now, the Rails and the Ruby API's, it's like I'm trying to read Greek!

Example?

I read the documentation, I think about it, I try to read it again, nothing, NOTHING is readable. I kno, I kno, might take a while to get used to. Still a newbie :-S

You need to provide a concrete example of what's confusing if you want help.

Best,