如何在循环遍历 Rails 中不同的关联元素时添加排序?
How can I add sorting while looping through distinct associated elements in Rails?
我有一个模拟邮票的测试应用程序 collection。有一个邮票目录,用户可以将其添加到 collection。 CollectionItem 模型有一堆 belongs_to:
belongs_to :stamp
belongs_to :issue
belongs_to :user
belongs_to :country
在 collection 索引视图中,有一个包含所有 collection 项的列表,它们以级联方式排列。
国家:发行:邮票。问题:邮票。问题:邮票。国家:...等等
@my_collection_countries = CollectionItem.where(user: current_user).select(:country_id).distinct(:country)
使用这种方法可以正常工作。
<% @my_collection_countries.each do |collection_item| %>
<%= link_to collection_item.country.name, country_path(collection_item.country) %>
<% collection_item.class.where(user: current_user, country_id: collection_item.country).select(:issue_id).distinct(:issue).each do |issue| %>
<%= link_to issue.issue.name, issue.issue %>
<% issue.class.includes(:stamp).where(user: current_user, country: collection_item.country, issue: issue.issue).select(:stamp_id).distinct(:stamp).order('stamps.name asc').each do |stamp| %>
<%= link_to stamp.stamp.name, stamp.stamp %>
<% end %>
<% end %>
<% end %>
但我认为我做错了什么,因为当我尝试以与邮票相同的方式对问题或国家进行排序时,它们会多次显示,并且 distinct
没有帮助。
我能想到的解决此问题的最佳方法是使用 #group_by
,一种遍历集合的 ruby 方法和 returns [=12] 形式的散列=].因此,在您的情况下,我们可以使用:
@my_collection_countries = CollectionItem.where(user: current_user).group_by(&:country).sort_by { |country,_| country.name }
<% @my_collection_countries.each do |country, xs| %>
<%= link_to country.name, country_path(country) %>
<% xs.group_by(&:issue).each do |issue, ys| %>
<%= link_to issue.name, issue %>
<% ys.group_by(&:stamp).sort_by { |stamp,_| stamp.name }.each do |stamp, zs| %>
<%= link_to stamp.name, stamp %>
<% end %>
<% end %>
<% end %>
因为现在你在 ruby 而不是 SQL 中完成所有工作,你不能再使用 #order
订购东西(这会在你的 SQL 查询),而是可以使用 #sort_by
来排序,正如我在示例的最内层循环中所演示的那样。
虽然此代码会对大型数据库造成显着的性能损失,但在小型测试应用程序中,这对您来说会很好。虽然这会比 SQL 解决方案慢,但它的一般复杂性是相似的,因为即使在 SQL 中你仍然会遇到问题,你将发出 CountryItem.where(user:current_user).count
SQL 查询这本身就很昂贵。
我有一个模拟邮票的测试应用程序 collection。有一个邮票目录,用户可以将其添加到 collection。 CollectionItem 模型有一堆 belongs_to:
belongs_to :stamp
belongs_to :issue
belongs_to :user
belongs_to :country
在 collection 索引视图中,有一个包含所有 collection 项的列表,它们以级联方式排列。
国家:发行:邮票。问题:邮票。问题:邮票。国家:...等等
@my_collection_countries = CollectionItem.where(user: current_user).select(:country_id).distinct(:country)
使用这种方法可以正常工作。
<% @my_collection_countries.each do |collection_item| %>
<%= link_to collection_item.country.name, country_path(collection_item.country) %>
<% collection_item.class.where(user: current_user, country_id: collection_item.country).select(:issue_id).distinct(:issue).each do |issue| %>
<%= link_to issue.issue.name, issue.issue %>
<% issue.class.includes(:stamp).where(user: current_user, country: collection_item.country, issue: issue.issue).select(:stamp_id).distinct(:stamp).order('stamps.name asc').each do |stamp| %>
<%= link_to stamp.stamp.name, stamp.stamp %>
<% end %>
<% end %>
<% end %>
但我认为我做错了什么,因为当我尝试以与邮票相同的方式对问题或国家进行排序时,它们会多次显示,并且 distinct
没有帮助。
我能想到的解决此问题的最佳方法是使用 #group_by
,一种遍历集合的 ruby 方法和 returns [=12] 形式的散列=].因此,在您的情况下,我们可以使用:
@my_collection_countries = CollectionItem.where(user: current_user).group_by(&:country).sort_by { |country,_| country.name }
<% @my_collection_countries.each do |country, xs| %>
<%= link_to country.name, country_path(country) %>
<% xs.group_by(&:issue).each do |issue, ys| %>
<%= link_to issue.name, issue %>
<% ys.group_by(&:stamp).sort_by { |stamp,_| stamp.name }.each do |stamp, zs| %>
<%= link_to stamp.name, stamp %>
<% end %>
<% end %>
<% end %>
因为现在你在 ruby 而不是 SQL 中完成所有工作,你不能再使用 #order
订购东西(这会在你的 SQL 查询),而是可以使用 #sort_by
来排序,正如我在示例的最内层循环中所演示的那样。
虽然此代码会对大型数据库造成显着的性能损失,但在小型测试应用程序中,这对您来说会很好。虽然这会比 SQL 解决方案慢,但它的一般复杂性是相似的,因为即使在 SQL 中你仍然会遇到问题,你将发出 CountryItem.where(user:current_user).count
SQL 查询这本身就很昂贵。