Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

print an empty table cell if method returns no data

I have the following code in a view partial to show a students average score for each evaluation period:

<tr class="<%= cycle("odd", "even", name: "students")%>">
    <td>
    <%= link_to "#{student.name}", 
                 student_path({student_group_id: student.student_group_id, id: student.id})%>
    </td>
    <% student.eval_count.times do |i| %>
      <td class="center"><%= student.avg_for_eval(i) %></td>
    <% end %>
    <td class="center"><%= student.avg unless student.avg.nan? %></td>
</tr>

I'm using this method in student.rb to generate the average score, and at first I couldn't figure out why I couldn't get it to generate empty rows when there was no data.

def evals
  evals = self.evaluations.order("eval_number").group_by(&:eval_number)
end

def eval_number_set(index)
  numbers = Evaluation.where('student_id = ?', self.id).uniq.pluck(:eval_number)
  numbers[index]
end

def avg_for_eval(i)
  scores = []
  evals = self.evals.select { |k, v| k == self.eval_number_set(i) }.values.first
  for eval in evals
    scores << eval.score
  end
  evals.empty? #(scores.sum.to_f / scores.size).round(2)  
end

I changed the last line of the method to evals.empty? as shown above and got this in the browser:

browser

Then I realized that because of select any evaluations the student was not present for (in the case of a new student) was not part of the dataset I was using to generate the code.

The problem, as you can see in the picture, is that the two students who only have data for the last evaluation don't have their data in the correct columns - everything has shifted to the left because no <td> is being produced by the view code.

So the question, then, is how can I rewrite the method code so that I get the same output, but if scores.empty? the method inserts "" or "no data" or some other placeholder so that a <td> is printed in the view?

update

I understand what's going on a bit better now. This method:

def eval_number_set(index)
  numbers = Evaluation.where('student_id = ?', self.id).uniq.pluck(:eval_number)
  numbers[index]
end

is returning the evaluation numbers for each student - so when those numbers are used in the next method

def avg_for_eval(i)
  scores = []
  evals = self.evals.select { |k, v| k == self.eval_number_set(i) }.values.first
  for eval in evals
    scores << eval.score
  end
  evals.empty? #(scores.sum.to_f / scores.size).round(2)  
end

it can only pull the evaluations that a student has been present for. back to the drawing board for now...

update 2

I've changed the class methods as follows:

#returns all 'eval_number's for a given group of students 
#as the first student will have been present for all evaluations

def eval_number_set(index)
  numbers = self.student_group.students.first.evals.keys
  # numbers = Evaluation.where('student_id = ?', self.id).uniq.pluck(:eval_number)
  numbers[index]
end

#attempts to match the first present 'eval_number' for a given student against
#the first number in the set of all 'eval_number's and react accordingly
def avg_for_eval(i)
  scores = []
  if self.evals.keys[i] == self.eval_number_set(i)
    "match"
  else
    "no_match"
  end  
end

this matched all the students who were present for every evaluation, but did not match any evaluations for the students who missed some. I changed the code to the following

  def avg_for_eval(i)
    scores = []
    if self.evals.keys[i] == self.eval_number_set(i)
      "#{self.evals.keys[i]} vs #{self.eval_number_set(i)}"
    else
      "#{self.evals.keys[i]} vs #{self.eval_number_set(i)}"
    end          
  end

and it returned the following in the browser:

new problem

so I tried to add a counter in the if statement such that if the statement matched, it would increment the key being tried, otherwise it would stay on the same key:

  def avg_for_eval(i)
    scores = []
    key_match = 0
    if self.evals.keys[key_match] == self.eval_number_set(i)
      "#{self.evals.keys[i]} vs #{self.eval_number_set(i)}"
      key_match += 1
    else
      "#{self.evals.keys[i]} vs #{self.eval_number_set(i)}"
    end  
  end

which produces this:

enter image description here

and makes sense - the count incrementing doesn't take any real effect because of the way it's being called. but while i think that I'm now closer to the effect I want, i'm not sure how to make it happen!

update 3

closer still...I've changed things so that the keys are read in reverse, so all of the data is being read - but I'd still like the data to be printed with the most recent on the right (see picture below). the model method code now looks like this:

  def eval_number_set(index)
    numbers = self.student_group.students.first.evals.keys.reverse
    # numbers = Evaluation.where('student_id = ?', self.id).uniq.pluck(:eval_number)
    numbers[index]
  end
  
  def avg_for_eval(i)
    scores = []
    eval_number = self.eval_number_set(i)
    if self.evals.keys.reverse[i] == eval_number
      for eval in self.evals.values[i] 
        scores << eval.score if self.evals.values[i]
      end
      scores
    else
      "no data"
    end  
  end

which returns the following, which I've annotated so you can understand what it is that I'm looking for a bit more clearly:

enter image description here

update 4

Calling student.evals returns the following (for student with id 32):

{29=>[
  #<Evaluation id: 1949, score: 3, created_at: "2013-08-28 09:44:32", updated_at: "2013-08-28 09:44:32", student_id: 32, goal_id: 63, eval_number: 29>, 
  #<Evaluation id: 1950, score: 4, created_at: "2013-08-28 09:44:32", updated_at: "2013-08-28 09:44:32", student_id: 32, goal_id: 64, eval_number: 29>, 
  #<Evaluation id: 1951, score: 5, created_at: "2013-08-28 09:44:32", updated_at: "2013-08-28 09:44:32", student_id: 32, goal_id: 65, eval_number: 29>], 
30=>[
  #<Evaluation id: 1957, score: 3, created_at: "2013-08-28 09:44:43", updated_at: "2013-08-28 09:44:43", student_id: 32, goal_id: 65, eval_number: 30>, 
  #<Evaluation id: 1956, score: 2, created_at: "2013-08-28 09:44:43", updated_at: "2013-08-28 09:44:43", student_id: 32, goal_id: 64, eval_number: 30>, 
  #<Evaluation id: 1955, score: 1, created_at: "2013-08-28 09:44:43", updated_at: "2013-08-28 09:44:43", student_id: 32, goal_id: 63, eval_number: 30>], 
31=>[
  #<Evaluation id: 1968, score: 2, created_at: "2013-08-28 11:26:56", updated_at: "2013-08-28 11:26:56", student_id: 32, goal_id: 70, eval_number: 31>, 
  #<Evaluation id: 1967, score: 2, created_at: "2013-08-28 11:26:56", updated_at: "2013-08-28 11:26:56", student_id: 32, goal_id: 69, eval_number: 31>, 
  #<Evaluation id: 1966, score: 1, created_at: "2013-08-28 11:26:56", updated_at: "2013-08-28 11:26:56", student_id: 32, goal_id: 68, eval_number: 31>, 
  #<Evaluation id: 1965, score: 1, created_at: "2013-08-28 11:26:56", updated_at: "2013-08-28 11:26:56", student_id: 32, goal_id: 67, eval_number: 31>, 
  #<Evaluation id: 1964, score: 1, created_at: "2013-08-28 11:26:56", updated_at: "2013-08-28 11:26:56", student_id: 32, goal_id: 66, eval_number: 31>, 
  #<Evaluation id: 1963, score: 3, created_at: "2013-08-28 11:26:56", updated_at: "2013-08-28 11:26:56", student_id: 32, goal_id: 65, eval_number: 31>, 
  #<Evaluation id: 1962, score: 3, created_at: "2013-08-28 11:26:56", updated_at: "2013-08-28 11:26:56", student_id: 32, goal_id: 64, eval_number: 31>, 
  #<Evaluation id: 1961, score: 3, created_at: "2013-08-28 11:26:56", updated_at: "2013-08-28 11:26:56", student_id: 32, goal_id: 63, eval_number: 31>], 
32=>[
  #<Evaluation id: 1983, score: 3, created_at: "2013-08-29 19:31:48", updated_at: "2013-08-29 19:31:48", student_id: 32, goal_id: 69, eval_number: 32>, 
  #<Evaluation id: 1982, score: 2, created_at: "2013-08-29 19:31:48", updated_at: "2013-08-29 19:31:48", student_id: 32, goal_id: 68, eval_number: 32>, 
  #<Evaluation id: 1981, score: 3, created_at: "2013-08-29 19:31:48", updated_at: "2013-08-29 19:31:48", student_id: 32, goal_id: 67, eval_number: 32>, 
  #<Evaluation id: 1980, score: 4, created_at: "2013-08-29 19:31:48", updated_at: "2013-08-29 19:31:48", student_id: 32, goal_id: 66, eval_number: 32>, 
  #<Evaluation id: 1979, score: 4, created_at: "2013-08-29 19:31:48", updated_at: "2013-08-29 19:31:48", student_id: 32, goal_id: 65, eval_number: 32>, 
  #<Evaluation id: 1978, score: 3, created_at: "2013-08-29 19:31:48", updated_at: "2013-08-29 19:31:48", student_id: 32, goal_id: 64, eval_number: 32>, 
  #<Evaluation id: 1977, score: 3, created_at: "2013-08-29 19:31:48", updated_at: "2013-08-29 19:31:48", student_id: 32, goal_id: 63, eval_number: 32>, 
  #<Evaluation id: 1984, score: 3, created_at: "2013-08-29 19:31:48", updated_at: "2013-08-29 19:31:48", student_id: 32, goal_id: 70, eval_number: 32>]
}
like image 508
dax Avatar asked Sep 10 '26 19:09

dax


1 Answers

UPDATE

You should change eval_count method or use constant value to make 4 cells. For example,

4.times do |i|
  <td class="center"><%= student.avg_for_eval(i) %></td>
end

END OF UPDATE

You can try this approach:

def avg_for_eval(i)
  scores = Array.new(4)
  evals = self.evals.select { |k, v| k == self.eval_number_set(i) }.values.first
  evals.each_with_index do |eval, i|
    scores[i] = eval.score
  end
  (scores.sum.to_f / scores.size).round(2)  
end

Pay attention to scores array initialization. In this method [nil,nil,nil,nil] is created initially. Scores would be equal to [first_score, second_score, nil, nil] for students with only two marks.

It leads to possible mistake: result of method would be

(first_score + second_score) / 4
like image 112
hedgesky Avatar answered Sep 13 '26 15:09

hedgesky



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!