【问题标题】:print an empty table cell if method returns no data如果方法没有返回数据,则打印一个空的表格单元格
【发布时间】:2013-08-30 11:51:55
【问题描述】:

我在部分视图中有以下代码,用于显示学生每个评估期的平均分数:

<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>

我在student.rb使用这个方法来生成平均分,一开始我想不通为什么在没有数据的情况下我不能让它生成空行。

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

如上图我把方法的最后一行改成evals.empty?,在浏览器中得到了这个:

然后我意识到,由于select,学生没有参加的任何评估(对于新学生来说)都不属于我用来生成代码的数据集的一部分。

正如您在图片中看到的那样,问题在于只有最后一次评估的数据的两个学生没有将他们的数据放在正确的列中 - 因为没有&lt;td&gt;,所以所有内容都向左移动了由视图代码生成。

那么问题是如何重写方法代码以获得相同的输出,但是if scores.empty? 方法插入"""no data" 或其他一些占位符以便&lt;td&gt; 是打印在视图中?

更新

我现在更好地了解发生了什么。这个方法:

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

它只能提取学生已经参加过的评估。现在回到绘图板......

更新 2

我已将类方法更改如下:

#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

这匹配了参加每次评估的所有学生,但不匹配错过一些学生的任何评估。我将代码更改为以下

  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

它在浏览器中返回以下内容:

所以我尝试在 if 语句中添加一个计数器,这样如果语句匹配,它将增加正在尝试的键,否则它将保持在同一个键上:

  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

产生这个:

并且是有道理的——计数递增不会产生任何实际影响,因为它被调用的方式。但是虽然我认为我现在更接近我想要的效果,但我不确定如何实现它!

更新 3

更接近...我已经改变了一些事情,以便反向读取键,所以所有数据都被 读取 - 但我仍然希望数据是 打印,最新的在右边(见下图)。模型方法代码现在如下所示:

  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

它返回以下内容,我已对其进行了注释,以便您更清楚地了解我正在寻找的是什么:

更新 4

调用student.evals 会返回以下内容(对于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>]
}

【问题讨论】:

  • "link_to "#{student.name}", student_path(...)"。此行可以重写为“link_to student.name, student_path(...)”
  • 不错不错,不知道为什么会这样
  • def avg_for_eval(index) Evaluation.where('student_id = ? and eval_number = ?', self.id, index).sum() end 这样的东西有用吗?
  • FWIW。我会避免在变量名中使用单词 eval。 IMO 它太接近保留字 eval 将执行您传递给它的任何 ruby​​ 代码。
  • 干杯,不知道这个

标签: html mysql ruby-on-rails ruby-on-rails-3


【解决方案1】:

更新

您应该更改 eval_count 方法或使用常数值来制作 4 个单元格。例如,

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

更新结束

你可以试试这个方法:

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

注意scores数组初始化。在这个方法中 [nil,nil,nil,nil] 最初被创建。只有两个分数的学生的分数将等于 [first_score, second_score, nil, nil]。

这导致可能的错误:方法的结果是

(first_score + second_score) / 4

【讨论】:

  • 我喜欢这个想法,但由于很多原因它仍然无法正常工作 - 我认为我需要选择以外的方法。
  • 请查看我的更新。我想,我找到了错误的原因。
【解决方案2】:

上次更新后,修改视图如下:

<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(student.eval_count - i) %></td>
<% end %>
<td class="center"><%= student.avg unless student.avg.nan? %></td>
</tr>

基本上,以相反的顺序显示它们

【讨论】:

  • 这本身并没有回答我的问题,但它是最好的答案,因为它最能引导我朝着正确的方向前进。
【解决方案3】:

我终于用下面的代码完成了这项工作 - 但如果有人能告诉我更好的方法来做到这一点,我很乐意给予他们赏金。

_student_list.html.erb

<table class="fixed">
  <tbody>      
    <% group.students.each_with_index do |student, index| %>
    <tr>
      <th></th>
      <% if index == 0 %>
        <% student.eval_count.times do |i| %>                                             
          <th class="center">Evaluation <%= i + 1 %></th>
        <% end %>
        <th><%= "Student average" if student.eval_count > 0  %></th>
      <% end %>

    </tr>
    <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.student_group.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>
    <% end %>
    <% reset_cycle("students") %>
  </tbody>
</table>

student.rb

  def eval_number_set(index)
    numbers = self.student_group.students.first.evals.keys
    numbers[index]
  end

  def reverse_eval_number_set(index)
    numbers = self.student_group.students.first.evals.keys.reverse
    numbers[index]
  end

  def avg_for_eval(i)
    scores = []
    eval_number = self.reverse_eval_number_set(i)
    count_differential = (self.student_group.eval_count - self.eval_count) - i
    if self.student_group.eval_count == self.eval_count
      for eval in self.evals.values[i] 
        scores << eval.score
      end
      (scores.sum.to_f / scores.size).round(2)
    else
      if self.evals.values[count_differential]
        for eval in self.evals.values[count_differential]
          scores << eval.score
        end
        (scores.sum.to_f / scores.size).round(2)
      else
        "no data"
      end  
    end  
  end

【讨论】:

  • groupstudent.student_group 是同一个对象吗?
  • 是的,在某些视图中有一个 each 块,其中每个 student_group 被迭代为 group
  • 那你为什么打电话给student.student_group.eval_count而不是group.eval_count?另外,你能举一些evals哈希的例子吗?
  • 关于 group.eval_count 的好消息 - 我将使用 evals hash 编辑帖子
【解决方案4】:

假设group.eval_counteval_count 的最大数量。

查看

<table class="fixed">
  <thead>      
    <tr>
      <th></th>
      <% group.eval_count.times do |i| %>                                             
      <th class="center">Evaluation <%= i + 1 %></th>
      <% end %>
      <th><%= "Student average" if group.eval_count > 0  %></th>
    </tr>
  </thead>
  <tbody>
    <% group.students.each do |student| %>
    <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>
      <% group.eval_count.times do |i| %>
      <td class="center"><%= student.avg_for_eval(i+1) %></td>
      <% end %>
      <td class="center"><%= student.avg unless student.avg.nan? %></td>
    </tr>
    <% end %>
    <% reset_cycle("students") %>
  </tbody>
</table>

如果您不想遍历所有评估编号(即从 29 开始),则使用(min..group.eval_count).each 之类的范围,您也可以反向执行(min..max).to_a.reverse.each。从您的数据中找出maxmin 应该相当简单。

student.rb

def avg_for_eval(i)
  # nil evaluated as false hence this works...
  if evaluations_with_sum[i]
     (evaluations_with_sum[i].sum / evaluations_with_sum[i].size).round(2)
  else
    'no data'
  end  
end

private
def evaluations_with_sum
  # memoize as hash using instance variable
  # 'order' does work with symbol and the sorting will be ASC
  @evals ||= self.evaluations.order(:eval_number).inject({}) do |hash, evaluation|
    hash[evaluation.eval_number] ||= []
    hash[evaluation.eval_number] << evaluation.score
    hash
  end
end

实际上我认为self.evaluations.order(:eval_number).average(:score).group(:eval_number) 应该给你一个以eval_number 为键和scoresum 作为值的哈希值,但我不太确定你可以尝试一下......

【讨论】:

    【解决方案5】:

    要打印一个空的表格单元格,请在单元格内放置一个不间断的空格。

     <td>&nbsp;</td>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-12
      • 1970-01-01
      • 2018-03-22
      • 1970-01-01
      • 2013-10-23
      相关资源
      最近更新 更多