🐥

Rails sort_byでいいねの並び替え

2023/04/30に公開

過去一週間でいいねの合計カウントが多い順に投稿を表示

アソシエーション

user.rb
has_many :books, dependent: :destroy
has_many :favorites, dependent: :destroy
book.rb
belongs_to :user
has_many :favorites, dependent: :destroy
has_many :favorited_users, through: :favorites, source: :user

has_many :favorited_users, through: :favorites, source: :user
はfavoritesテーブルを介してuserテーブルに関連するUserモデルのコレクションを取得するという意味!

favorite.rb
belongs_to :user
belongs_to :book

コントローラー

いいね数が多い順で表示させるためにコントローラー内で条件を定義。

book_controller.rb
def index
    to = Time.current.at_end_of_day
    from = (to - 6.day).at_beginning_of_day
    @books = Book.includes(:favorited_users).
      sort_by {|x|
        x.favorited_users.includes(:favorites).where(created_at: from...to).size
      }.reverse
    @book = Book.new
  end

〜解説〜

to = Time.current.at_end_of_day
現在の日時を表す Time.current を当日の終わりの時間に設定。
at_end_of_day は ActiveSupport::TimeWithZone クラスのメソッドで、その日の終わりを表す時刻(23:59:59.999999)を返している。

from = (to - 6.day).at_beginning_of_day
at_beginning_of_day は1日の始まりの時刻を0:00に設定している。

@books = Book.includes(:favorited_users).

今までは@books = Book.all で投稿されたデータを全て取得し、誰が投稿したかという情報をユーザーテーブルに毎回取りに行っていた。

無駄にSQL文が発行されなくするために、先程book.rbで記述した
has_many :favorited_users, through: :favorites, source: :user
ユーザーの情報がfavorited_usersに格納され、それをincludesの引数に入れてあげることで、事前にユーザーのデータを読み込ませてあげることができる!

sort_by {|x| x.favorited_users.includes(:favorites).where(created_at: from...to).size }.reverse

favorited_usersによって関連づけられたユーザーから、from から to の期間に作成された favoritesの数に基づいて、与えられた配列xの要素を並べ替えている。

ブロック変数 |x| を定義しないと、2よりも10,11の方が小さいと判定されてしまう。

最後にreverseをつけることで、昇順ではなく、降順で表示される!


sortとsort_byの違い
https://zenn.dev/d_publishing/articles/76f1a4a047adce

sortは2要素を比較するたびに計算が必要で、一方sort_byにおける計算回数は要素数だけ。
パフォーマンスの観点から原則sortよりもsort_byを利用するほうが好ましい
https://nishinatoshiharu.com/ruby-sort-array/

🌱参考にさせていただいた記事
https://qiita.com/ooyama-tetu/items/1e19fea32908a6a737f5

Discussion