user_2782109 user_2782109 - 6 months ago 11
Ruby Question

How to create a non-static HTML table header and rows

I have two arrays, and I'd like to create a table with dynamic header cells (from the first array called

subjects
) and iteratively add contents (from the second array called
examscores
) in table rows with respect to the table header value.

Desired outcome is (fiddle):

enter image description here

The erb code is:

<table width="100%" border="1">
<thead>
<tr>
<th rowspan="2" scope="col">NAME</th>
<th colspan="<%= @subjects_by_class.size %>" scope="col">Subjects/Scores</th>
<th rowspan="2" scope="col">Total</th>
<th rowspan="2" scope="col">Average</th>
</tr>
<tr>
<% @subjects_by_class.each do |s| %>
<th> <%= s.name %></th>
<% end %>
</tr>
</thead>
<tbody>
<% @examscore.each do |ex| %>
<tr>

<td><%= get_student_name_by_id(ex.student_id) %></td>

<% @subjects_by_class.each do |ss| %>
<% @examscore.each do |ii| %>

<% if ss.id == ex.subject_id %>
<td> <%= i.total %> </td>
<% break %>
<% end %>

<% end %>
<% end %>

<td><%= sum_student_totalscore(ex.student_id, year_id) %> </td>
<td><%= avg_student_totalscore(ex.student_id, year_id) %></td>
</tr>
<% end %>
</tbody>
</table>


The output I get is (fiddle):

enter image description here

A new
tr
is created under
Maths
subject instead of a new
td
for
Arts
subject, and this results in
Average
td
being distorted.

Any insight will be greatly appreciated.

Answer

Well, just look at this section of your code:

<% @examscore.each do |ex| %>
 <tr>

You create a new line for each @examscore, and you have 4 of those (1 per user/subject, so you end up with 4 lines of course).

Your tbody should be something like this:

<tbody>
  <% @students.each do |student| %>
    <tr>
      <td><%= student.name %></td>
      <% @subjects_by_class.each do |subject| %>
        <% @examscore.each do |score| %>
          <% if score.subject_id == subject.id && score.student_id == student.id %>
            <td><%= score.total %></td>
            <% break %>
          <% end %>
        <% end %>
      <% end %>
      <td><%= sum_student_totalscore(student.id, year_id) %> </td>
      <td><%= avg_student_totalscore(student.id, year_id) %></td>
    </tr>
  <% end %>
</tbody>

It's a bit strange that you only care about the year in your totals

Also you could improve things a little bit by having a method in your Student class that returns an array of scores for a given year/list of subjects

# Returns an array of scores for the given year
def scores(year, subject_ids)
  subject_ids.map do |subject_id|
    # find score for year & the given subject_id
  end
end

This way your body would become

<tbody>
  <% @students.each do |student| %>
    <tr>
      <td><%= student.name %></td>
      <% @scores = student.scores(year_id, @subjects_by_class) %>
      <% @scores.each do |score| %>
        <td><%= score.total %></td>
      <% end %>
      <% scores_total = @scores.sum(&:total) %>
      <td><%= scores_total %> </td>
      <td><%= scores_total / @scores.size.to_f %></td>
    </tr>
  <% end %>
</tbody>

which I found more clear, but it could be further improved with decorators for instance.