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,