0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[rails 初心者]同率順位を踏まえたランキング機能

Posted at

初めに

ポートフォリオテーマとして業者のレビューサイトを作成しています。
以下の画像のようなレビューサイトを作っていこうと思います。

top画面2-1.png

※自分用でまとめてますので、分かり辛かったらすいません。
また理解不足・誤りがあったらコメント頂けますと幸いです。m(__)m<ヨロシクオネガイシマス

前提条件

①今回の記事ではランキング機能のみ(※同率順位を踏まえた順位)についてまとめます
②レビュー機能や並び替え機能については、以下URLの記事に記載されている内容を参考にしてください

[rails初心者]複数権限でのログインを使ったレビューサイトの作成(基本)
[rails初心者]複数権限でのログインを使ったレビューサイトの作成(応用)

今回作成するランキング機能について

・同率順位がある場合、同じ順位として表示すること
・すべての業者に対してランキング機能を実装する(ページネーションの移動後でもその順位は継承されていること)

主なやること

・コントローラの編集(配列の付け足し)
・モデルの編集(平均値算出処理の編集)
・ビューページの編集(配列が付け足されたことによる修正作業)

この3点です

開発環境

・rails 6.1.7
・ruby 3.1.2
・raty 3.1.1
・jquery 3.6.1
・bootstrap 4.5

コントローラの記述

配列の付け足し

今回はmapメソッドを使って次のようなメソッドを作っていきます

app/controllers/平均値を表示したいファイルに関係するコントローラ
  def アクション名
    same_rate = 0
    rank = 1
    traders = Trader.find(Review.group(:trader_id).order('avg(star) desc').pluck(:trader_id)).map.with_index(1) do |trader, i|
    if i == 1 #ランキング処理の一番最初だけ以下の処理を行う
      same_rate = trader.review_average #一番最初だけ「same_rate」に値が入っていないので、先に値を入れる
    end
    if trader.review_average != same_rate #同率順位がない場合以下の処理
      rank = i #同率が無いので、順位を更新
      same_rate = trader.review_average #「same_rate」に現在の値を代入 → 次の値と比べる用として運用するイメージ
      {trader: trader, index: rank } #[trader = trader]という配列と[index = rank]という配列それぞれをセットにしている
    else #同率順位がある場合以下の処理
      same_rate = trader.review_average
      {trader: trader, index: rank }
    end
  end

  @traders = Kaminari.paginate_array(traders).page(params[:page]).per(5)
  #Kaminari.paginate_array → pageメソッドを配列に対して用いる場合に必要な記述

解説1

まずはsame_rate = 0 , rank = 1の部分についてです。
使う意味合いとしては次の通りです。

ポイント①同率順位を考慮する=same_rateを使って同率かどうかを調べる
ポイント②順位の表示=rankを使って順位を表示させる

そしてこの行でやっていることは次の通りです。
same_rate = 0 , rank = 1の部分では、これらの変数の初期値を最初に決めている感じです。

解説2

次にtraders =Trader.find( 省略 ).map.with_index(1) do |trader, i|の部分です。
使う意味合いとしては次の通りです。

ポイント①もともとある配列(Trader.find( 省略 ))に別の配列を付け足す(.map)
ポイント②新しい方の配列は1からスタートさせたい(.with_index(1))
ポイント③変数は2種類(|trader, i|)

補足説明

実はmapメソッドは今回が初めてでした (´▽`)テヘペロ
ということで簡単にまとめると

mapメソッド == each+処理を実行 だと思いました( ..)φメモメモ
※詳しくは参考サイトのリンクを参照してくださいm(__)mオネガイシヤス

基本的な書き方
    配列変数.map {|変数名| 具体的な処理 }

今回のmapメソッドさんのシナリオ(やって欲しい挙動)は次の通りです。
もともとある配列(Trader.find( 省略 ))に別の配列(.with_index(1))を付け足す(.map)。
その際使う変数は2種類(|trader, i|)とする。

また新しく作る配列(.with_index(1))には書き方が二種類あるらしいです。
今回は「1」からスタートしたかったので、.with_index(1)を選びました。

ここまでの情報をまとめてみるとこんなイメージです。字が汚いのはすいません(;^ω^)
mapメソッド.jpg

以下が参考記事のリンクです。
【Rails入門】mapメソッドを完全攻略!配列操作の基礎を学ぼう
【Ruby】インデックス有りのループ処理を行う場合、each_with_indexよりもwith_indexの方が便利だった

解説3

続いては if の条件部分trader.review_average != same_rateです。
使う意味合いとしては次の通りです。

ポイント①同率順位がいるかどうかを調べている
ポイント②trader.review_averageには今回のチェック対象の平均値が入っている
ポイント③same_rateには前回チェック時に対象が持っていた平均値が入っている

つまりこんな感じです。
「前回の平均値 == 今回の平均値」

または
「前回の平均値 != 今回の平均値」

どっちでもOK(処理の中身が逆になるだけなので)

解説4

続いてsame_rate = trader.review_average{trader: trader, index: rank }です。
これらの意味合いとしては次の通りです。

ポイント①same_rateの更新
ポイント②|trader, i|に入る値を決めている

ポイント①について・・・

先程の解説3の時に同率順位かどうかをチェックしたと思います。
このチェックでは「一つ前の順位と同じか?」という見えない条件があり、
それをクリアするためには随時更新作業が必要になります。
なのでsame_rate = trader.review_average処理が記述されているわけです。

また{trader: trader, index: rank }については、次の画像のように「配列1と配列2」それぞれにどの値を代入するかを決めているだけです。
mapメソッド2.jpg

モデルの編集(平均値算出処理の編集)

平均値算出処理の編集

まず前回紹介した平均値算出用に作ったメソッドがこちらです

app/models/trader.rb
  ##評価点の平均値
  def review_average(trader)
    if trader.reviews.count >= 1 #顧客(client)に評価されたことがあるか否かを確認
      reviews=Review.where(trader_id: trader.id).pluck(:star)#starカラムに保存されている評価点数を取得
      #reviews.map!(&:to_i)#取得した変数(reviews)が文字列の場合、数列型へ変更しましょう
      reviews.sum.fdiv(reviews.length).round(1)#取得した変数をもとに平均値を算出
      #round(1)とすることで小数点第2位に対して四捨五入をします。
    else
      0.0 #顧客(client)に評価されたことが無い場合、平均値を0.0に設定
    end
  end

これを次のように書き替えます。

app/models/trader.rb
  ##評価点の平均値
    def review_average
    if reviews.count >= 1
      reviews=Review.where(trader_id: id).pluck(:star)#starカラムに保存されている評価点数(文字列型)を取得
      reviews.map!(&:to_i)#取得した変数(reviews)を数列型へ変更する
      reviews.sum.fdiv(reviews.length).round(1)#取得した変数をもとに平均値を算出
      #round(1)とすることで小数点第2位に対して四捨五入をします。
    else
      0.0
    end
  end

主な変更点

・メソッド名の横にある引数の(trader)を排除
・引数が無くなったので、レシーバ( . の左の文字)はselfになる
・ただselfはレシーバである場合、省略可能

やることは以上になります。

ビューページの編集

配列が付け足されたことによる修正作業

結論からいうと、次のような記述になります。

app/views/平均値を表示したいファイル
<% @traders.each do |trader| %>
    <div>第<br><%= trader[:index] %><br>位</div>
    <% if trader[:trader].profile_image.attached? %>
        <%= image_tag(trader[:trader].get_profile_image(100,100)) %>
    <% else %>
        <%= image_tag 'leader_sagyouin_woman.jpg', size: "100x100" %>
    <% end %>
    <%= trader[:trader].name %>
    <%= trader[:trader].email %>
    <%= trader[:trader].telephone_number %>
    <%= trader[:trader].genre.name %>
    <div>評価平均:<%= trader[:trader].review_average %></div>
<% end %>
<%= paginate @traders %>

主な変更点

・変数(trader)の隣に配列([:index] or [:trader])を入れる

なぜ配列を記述しないといけないのか?

まずeach文で使っている @tradersには画像のtraders(ローカル変数)と同じポジションになります。
※コントローラ内で代入しているため

それを踏まえて次の画像を見てみましょう。

tradersという 大枠の中に配列①と配列②の 中枠が二つあります。

この中枠が2種類存在することで、どちらの枠(配列)なのかを宣言しないといけないからです

mapメソッド.jpg

以上で、やることは終わりました。
bootstrapの書き方などを整えると次の様なサイトが出来上がりました。

top画面2-2.png
top画面2-3.png
top画面2-4.png

ページネーションで飛んだとしても順位は引き継がれているので、ランキング機能の実装が完了しました。

さいごに

今回PF作成のテーマとしてレビューサイト(+ランキング機能)を作ってみました。
まだ学習中の身なので理解不足・誤りがあったらコメント頂けますと幸いです。m(__)m<ヨロシクオネガイシマス

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?