LoginSignup
1
0

More than 1 year has passed since last update.

Railsランキング機能(同率含む)

Posted at

Railsでレビュー評価に基づくランキング機能の実装方法です。ランキングの作り方は他にも記事が出ていますが、同率(例えば1位が2つ合った場合、次が3位になる)の実装方法の記事はあまり見られなかったので、記事を残したいと思います。

条件

  • 投稿(post)の評価(1~5)の平均に応じた降順のランキング
  • Userモデル,Postモデル(投稿)が多対多の関係であり、中間テーブルのReview(口コミ)モデルが存在する
  • ルーティングは任意

STEP1
任意のコントローラーにアクションを作ります。今回はrankingにしました。アクション内で評価に基づくデータの所得のコードを記入します。また、Reviewモデルの対象カラムはrateです。取得したデータを@rankに格納します。
データの取得方法ですが2つあります。

ActiveRecordのメソッドを利用する場合

@ranks=Post.find(Review.group(:post_id).order('avg(rate) desc').pluck(:post_id))

ポイント
Reviewモデルに対して.group(:post_id)でPostモデルのidごとにグループ化します。平たく言うと、Postモデルのidごとに口コミを集計します。続いて、.order('avg(rate) desc')で評価を平均にし、降順で並べます。そして、.pluck(:post_id))でReviewモデルからPostモデルのidを取得し、Postモデルのデータを@rankに格納します。

Rubyのsortメソッドを利用する場合

@ranks=Post.all.sort {|a,b| b.reviews.average(:rate) <=> a.reviews.average(:rate)}

ポイント
これはPostモデルにhas_many :reviewsを追加した場合に使用できます。Postモデルの各要素の評価の平均値で.sortメソッドを使い、並べ替えています。

STEP2
ビューファイルを作成し、コードを記述します。同率を実現するために、<% last_rate = 0 %>,<% j = 1 %>を用意します。last_rateが前回の口コミの評価平均値、jが順位に使うものになります。
そして、last_rateと今回の評価平均値でif文で分岐をしていく形です。どちらも最後に今回の値をlast_rateに格納します。それで.each文でループさせていくことにより、同率を含むランキングが実現できます。

<% last_rate = 0 %>
<% j = 1 %>
  <% @ranks.each.with_index(1) do |post,i|%>
    <% if post.reviews.average(:rate).to_f.round(1) != last_rate %>
      <% j = i %>
      <div>
       <%=j%> 位<%=post%>
      </div>
      <% last_rate = post.reviews.average(:rate).to_f.round(1) %>
    <% else %>
      <div>
       <%=j%> 位<%=post%>
      </div>
      <% last_rate = post.reviews.average(:rate).to_f.round(1) %>
    <% end %>
  <% end %>
</div>
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0