我正在为一种常见的场景寻找最佳实践:将从Rails(ActiveRecord,SQL)数据库表中提取的稀疏记录按摩成一种便于在HTML中呈现表的结构。
出于性能原因,我做了一个返回数据的查询,如下所示(为了清晰起见,我简化了示例):
Lineitem.all
=> [#<Lineitem id: 1, username: "Bob", category: "A", amount: 10>,
#<Lineitem id: 2, username: "Bob", category: "C", amount: 20>,
#<Lineitem id: 3, username: "Jen", category: "A", amount: 30>,
#<Lineitem id: 4, username: "Ken", category: "B", amount: 40>,
#<Lineitem id: 5, username: "Ken", category: "E", amount: 50>]
我的目标是这样一个HTML表:
A B C D E
--- --- --- --- ---
Bob 10 20
Jen 30
Ken 40 50
Sam
如果每个类别都作为单独的列存储在数据库中(或者如果我使用NoSQL…?!),或者如果我不关心数据库性能,这将是微不足道的。
为了解决这个问题,我一直在写这样的有气味的助手代码:
# create hash lookup, index first by username then by category, eg:
# ["Bob"]["A"] = #<Lineitem id: 1, ...>
# ["Bob"]["C"] = #<Lineitem id: 2, ...>
# ["Jen"]["A"] = #<Lineitem id: 3, ...> ...
def index_lineitems(lineitems)
h = {}
lineitems.each do |li|
h[li.username] = {} unless h.key? li.username
h[li.username][li.category] = li
end
h
end
# look up value from indexed hash
def get_lineitem_amount(indexed_lineitems, username, category)
if indexed_lineitems.key?(username) && indexed_lineitems[username].key?(category)
indexed_lineitems[username][category].amount
else
""
end
end
或者在这方面的一些变化。然后,我确定行和列的最终列表(注意"Sam"行…),并通过每次循环和调用get_lineitem_amount
来呈现HTML表。这是如此糟糕的代码,我很不好意思分享它。
当然,对于这个常见的问题,有一种更干净、更OO和Rails友好的方法。
有什么建议吗?
我正在做一些非常相似的稍微干净的事情:
假设这是在控制器中:
@data = LineItem.all
这是的视图
columns = @data.map(&:category).uniq
%table
%thead
%tr
%th
- columns.each do |column|
%th= column
%tbody
- @data.group_by(&:username).each do |username, rows|
%tr
%td= username
- cursor = 0
- rows.group_by(&:category).sort_by{|cat,rows| columns.index(cat)}.each do |category, rows|
- until cursor == columns.index(category) do
- cursor += 1
%td
%td= rows.sum(&:amount)
如果您将列存储在一个单独的DB表中,并将它们包含到当前模型中,这样您就可以将索引位置直接存储在对象上,而不需要动态计算它们,也不需要很好地控制顺序,那么它会变得更干净。一个额外的查询并不会真正影响应用程序的性能。