0

I have a collection of products users have purchased, grouped by their name, so I can count the number of unique products and how many of each has been purchased:

Controller:

@line_items = Spree::LineItem.joins(:order).where(spree_orders: {state: "complete"})
@products = @line_items.group_by(&:name)

View:

<% @products.each do |name, line_items| %>
     <%= name %> - <%= line_items.count %><br>
<% end %>

Is there a way to order the .each loop so that it descends by line_items.count?

Thanks

Alex
  • 95
  • 1
  • 8

1 Answers1

2

It will perform better getting the correct data directly from the db:

@products = @line_items.group(:name).order("count_all DESC").count

That will give you the names and counts directly, e.g.

# => { "line_1" => 3, "line_2" => 2, "line_3" => 8 }

There's a bit of Rails magic at work here: the SQL generated using group, order and count will look like:

SELECT COUNT(*) AS count_all, name AS name FROM spree_line_items GROUP BY name ORDER BY count_all DESC

That's where count_all comes from: Rails attaches it to the count column automatically.

Then you can plug this directly into your view:

<% @products.each do |name, line_item_count| %>
  <%= name %> - <%= line_item_count %><br>
<% end %>

Alternatively, if you're using the instance variable elsewhere, here's a simple Ruby solution:

@products = @line_items.group_by(&:name).sort_by { |_k, line_items| line_items.count }.reverse

This simply uses sort_by to get records ordered by the relevant count, then reverses to get decending order. There's a good benchmark on doing this here.

Hope that helps - let me know how you get on / if you've any questions.

SRack
  • 11,495
  • 5
  • 47
  • 60