我在视图中有以下代码,以显示每个评估期的学生平均分数:
<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
学生没有参加的任何评估(在新学生的情况下)不是我用来生成代码的数据集的一部分。
正如您在图片中看到的那样,问题在于,只有最后一次评估数据的两名学生的数据没有在正确的列中 - 所有内容都向左移动,因为没有{{1}正在由视图代码生成。
那么问题是,如何重写方法代码以便获得相同的输出,但<td>
方法会插入if scores.empty?
或""
或其他一些占位符,在视图中打印"no data"
?
我明白现在有什么好转的。这个方法:
<td>
返回每个学生的评估数字 - 所以当这些数字用于下一个方法时
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
这匹配了所有参加评估的学生,但是对于错过了一些评分的学生的评价不符。我将代码更改为以下
#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
并在浏览器中返回以下内容:
所以我试着在if语句中添加一个计数器,这样如果语句匹配,它会增加正在尝试的键,否则它将保持在同一个键上:
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
产生这个:
并且有意义 - 计数递增不会因为它被调用的方式而产生任何实际效果。虽然我认为我现在更接近我想要的效果,但我不确定如何实现它!
更近了......我改变了一些东西,以便反向读取键,所以所有的数据都是读 - 但我仍然希望数据打印,右边是最近的(见下图)。模型方法代码现在看起来像这样:
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
返回以下内容,我已对其进行了注释,以便您能够理解我正在寻找更清楚的内容:
致电 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
会返回以下内容(适用于student.evals
32的学生):
id
答案 0 :(得分:3)
<强>更新强>
您应该更改 eval_count 方法或使用常量值来制作4个单元格。例如,
4.times do |i|
<td class="center"><%= student.avg_for_eval(i) %></td>
end
END OF UPDATE
您可以尝试这种方法:
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
注意得分数组初始化。在这个方法中,最初创建[nil,nil,nil,nil]。对于只有两个分数的学生,分数等于[first_score,second_score,nil,nil]。
导致可能的错误:方法的结果将是
(first_score + second_score) / 4
答案 1 :(得分:0)
上次更新后,请修改此视图:
<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>
基本上,按逆序显示
答案 2 :(得分:0)
我最终使用下面的代码完成了这项工作 - 但如果有人能告诉我更好的方法,我很乐意给予他们赏金。
_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
答案 3 :(得分:0)
假设group.eval_count
是eval_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
执行此操作。从您的数据中找出max
和min
应该相当简单。
<强> 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)
应该以{{1}}作为关键字和eval_number
的总和作为值给出一个哈希,但我不太确定你能不能尝试一下......
答案 4 :(得分:0)
要打印空表格单元格,请在单元格内放置一个不间断的空格。
<td> </td>