質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Q&A

解決済

3回答

2095閲覧

nilを含むソートの表示が上手くいかない。「undefined method `sort_by' for nil:NilClass」

punchan36

総合スコア105

Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

0グッド

0クリップ

投稿2020/12/30 07:10

前提・実現したいこと

LINEのようなメッセージ機能を持つ「トークルーム一覧」において、メッセージが作成された順で各トークルームを降順に並べ替える実装を施しました。

しかし、メッセージの授受がまだ行われていないトークルームをユーザーが保持している場合、そのユーザーのトークルーム画面はエラー表示になると言う問題が起こっています。

空のトークルームデータを削除し、メッセージのやり取りのあるトークルームのみを残すと、エラーメッセージもなくページが正常に表示されます。

該当のソースコード

Rooms_controller.rb

Ruby

1 def set_another_entries 2 # ログイン中のユーザーの持つEntryを配列にする。 3 my_room_ids = @current_user.entries.pluck(:room_id) 4 # 上で定義した「ログイン中のユーザーの持つEntry」の内、「@current_userと異なるuser_id」、つまり自分以外の相手一覧を表示する。 5 @another_entries = Entry.includes(:user, :room).where(room_id: my_room_ids).where('entries.user_id != ?', @current_user.id) 6 7 # エントリーを配列にし、各エントリーを「メッセージが作成された順」で並べ替える。reverseでエントリーを降順にする。 8 @entries = @another_entries.to_a.compact!.sort_by {|entry| entry.messages.order(created_at: :desc).first&.created_at}.reverse 9 end
NoMethodError in RoomsController#index undefined method `sort_by' for nil:NilClass Extracted source (around line #67): 66 67 @entries = @another_entries.to_a.compact!.sort_by {|entry| entry.messages.order(created_at: :desc).first&.created_at}.reverse 68

Entryとは UserRoomを繋ぐ中間テーブルで、これをビューにて繰り返し表示しています。
ER図

ここに至るまでの過程

ここに至るまでにも何度かエラーが出てきましたので、どのように対策したかを書いていきます。

①最初、該当のコードは以下の記述でした。

ruby

1@entries = @another_entries.to_a.sort_by {|entry| entry.messages.order(created_at: :desc).first.created_at}.reverse
NoMethodError in RoomsController#index undefined method `created_at' for nil:NilClass Extracted source (around line #67): 66 67 @entries = @another_entries.to_a.sort_by {|entry| entry.messages.order(created_at: :desc).first.created_at}.reverse 68

created_at がnilと言う事だったので、その前に&.を付けました。

ruby

1@entries = @another_entries.to_a.sort_by {|entry| entry.messages.order(created_at: :desc).first&.created_at}.reverse
ArgumentError in RoomsController#index comparison of NilClass with ActiveSupport::TimeWithZone failed Extracted source (around line #67): 66 67 @entries = @another_entries.to_a.sort_by {|entry| entry.messages.order(created_at: :desc).first&.created_at}.reverse 68

③配列に格納されている要素の中で「nil」がある事が原因(?)と思ったので、これを取り除く compact メソッドを使いました。

ruby

1@entries = @another_entries.to_a.compact!.sort_by {|entry| entry.messages.order(created_at: :desc).first.created_at}.reverse
NoMethodError in RoomsController#index undefined method `sort_by' for nil:NilClass Extracted source (around line #67): 66 67 @entries = @another_entries.to_a.compact!.sort_by {|entry| entry.messages.order(created_at: :desc).first&.created_at}.reverse 68

ここで冒頭にも記述致しましたエラー文が表示され、対策が分からず足踏みしている状況です。
どなたかご助言頂けますと有難いです。

補足情報(FW/ツールのバージョンなど)

ruby 2.6.4p104
RubyGems 3.0.3
Rails 5.2.3

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

末尾に!をつける破壊的メソッドの多くは変更が行われない場合にnilを返しますので
メソッドチェインの中に入れる場合に注意が必要です。

compact!のリファレンス

投稿2020/12/30 07:32

asm

総合スコア15149

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

punchan36

2020/12/30 11:19

有難うございました! メソッドに関する理解が深まりました。
guest

0

ベストアンサー

エラーの直接の原因は回答が付いている通り、compactcompact!の使い間違いです。

③配列に格納されている要素の中で「nil」がある事が原因(?)と思ったので、

思ったのに確かめもせずにプログラム修正したのが間違いですね。状況を混乱させる選択です。

to_aの結果にnilが含まれていたら、entry.messagesの段階でentrynilになるので、undefined method 'messages' for nil:NilClassのエラーになるはずですよね。

nilなのはfirstの結果で、その原因はorder結果が空リストつまりmessage結果が空なのでしょう。

messageがないentryをどうしたいのでしょうか?

もしそのentryを除外したいのなら、sort_byの前でrejectselectで除外すればいいでしょう。
もし除外せずに、最新メッセージの日時以外のキー(今現在とか)を与えたいなら、sort_byのブロック中でifでそういう処理にすればいい。
あるいは、「entryには必ずmessageがあるはず」ということなら、もっと前の段階で間違ってそうでないデータが出来てしまっています。

②created_at がnilと言う事だったので、その前に&.を付けました。

こういう延髄反射みたいな修正は駄目ですね。コーディングレベルのミスじゃなくて設計ミスなので。

投稿2020/12/30 10:24

otn

総合スコア85901

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

punchan36

2020/12/30 11:17

有難うございます! 仰る通り一つ一つの修正の仕方が間違っておりました…。 以下のコードで希望の実装が出来ました。 @entries = @another_entries.includes(:messages).to_a .reject {|entry| entry.messages.blank?} .sort_by {|entry| entry.messages.order(created_at: :desc).first.created_at} .reverse メッセージデータがまだない Entry は非表示にしたかったので、reject で対応致しました。 有難うございました!
guest

0

エラーメッセージは、「undefined method `sort_by' for nil:NilClass」なので、質問のプログラムで、.sort_byメソッドが使わている箇所を探してみると

@entries = @another_entries.to_a.compact!.sort_by

のように、compact! に続いて、sort_byが使われているコードがあります。

compact!は、配列から nil である要素を削除した配列を返すという機能と、配列に nil が含まれていない場合には nil を返すという(余計な)機能がありますから、.compact! の前の配列に nil を含まないものが存在していることがエラーの原因だと推測されます。

[対処方法]、.compact!した結果が nil かどうかを判断して、
・nilだった場合は .compact!する前の配列を .sort_by する。
・nilでなかったら、その配列を .sort_byする。
というようにプログラムを修正してください。そうすれば問題が解決すると思います。

投稿2020/12/30 07:29

fumu7

総合スコア121

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

winterboum

2020/12/30 08:06

compact! でなく  compact にしてもらったら?
punchan36

2020/12/30 09:41

有難うございます! compact! 前の配列が nil かどうか調べた所、以下の様になりました。 [8] pry(main)> @another_entries.to_a.compact!.nil? => true 配列に nil が含まれていない為、nilを返したと言う事だと思います。 ただその場合の「.compact!する前の配列を .sort_by する」の部分で、.compact!をどこに置けば良いのか分かりませんでした…。申し訳ありません。 @another_entries.to_a.sort_by {|entry| entry.messages.order(created_at: :desc).first&.created_at}.reverse これは違いますよね…。 ちなみに「!」を付けずに nil 判定を行うと false でした。 [9] pry(main)> @another_entries.to_a.compact.nil? => false [10] pry(main)> @another_entries.to_a.nil? => false
punchan36

2020/12/30 11:21

無事解決致しました。有難うございました! compact! の使い方も間違っていましたが、それ以前の部分でつまずいておりました。 以下のコードで希望の実装が出来ました! @entries = @another_entries.includes(:messages).to_a .reject {|entry| entry.messages.blank?} .sort_by {|entry| entry.messages.order(created_at: :desc).first.created_at} .reverse
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問