4
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

【Ruby/Rails】シンプルに書ける小技メモ

自分がほえーと思った小技などをメモしておく。
全てがベストプラクティスというわけではないので、状況に応じて使い分けたい。

(4/18追記)
コメントより、「もっと簡単な書き方がある」とご教示いただきましたので追加しました。ありがとうございました。

Safe Navigation Operator(ぼっち演算子)

Bad
articles = user.articles if user
Good
articles = user&.articles

user.articlesだと、userがnilになる場合にNoMethodErrorエラーが出る。そのため、userがあるかどうかifで判定しようとするが、ぼっち演算子を使うとシンプルに表記できる。
userがnilの場合はuser&.articlesもnilになる。

user&.articles&.take&.tag&.nameのようにメソッドチェーンが長くなってもif文がネストすることがない。
ただし、user&.articles.take.tag.nameのようにすると、結局articles.takeのところでNoMethodErrorになるので注意。
NEWS for Ruby 2.3.0 (Ruby 2.7.0 リファレンスマニュアル)

Hash#dig

data = { user: { name: '太郎',
                 tag: { id: 1,
                        name: "タグ" }}}
[]をつなげた場合
data[:user][:tag][:name]
=> "タグ"

data[:user][:article][:name] # :articleが存在しない=>nilの場合
Traceback (most recent call last):
        1: from (irb):9
NoMethodError (undefined method '[]' for nil:NilClass)
digを用いた場合
data.dig(:user, :tag, :name)
=> "タグ"

data.dig(:user, :article, :name)
=> nil

ネストされたハッシュやjsonからデータを取り出したいときに使える。途中のデータがnilのときに自動的にnilを返してくれる。
ぼっち演算子とイメージは少し似ている。
Hash#dig (Ruby 2.7.0 リファレンスマニュアル)

hoge.presence || 'hogeは何も書いてないよ'

if hoge.present?
  hoge
else
  'hogeは何も書いてないよ'
end

hoge.present? ? hoge : 'hogeは何も書いてないよ'

hoge.presence || 'hogeは何も書いてないよ'

presenceは該当のオブジェクトがあればそれを返し、blankな値の場合はnilを返す。

[4] pry(main)> 'aaa'.presence
=> 'aaa'
[5] pry(main)> nil.presence
=> nil
[6] pry(main)> ''.presence
=> nil
[7] pry(main)> false.presence
=> nil

render(objects) || message

<% if @articles %>
  <%= render @articles %>
<% else %>
  <%= "記事はありません" %>
<% end %>
<%= render(@articles) || "記事はありません" %>

記事がないときのメッセージをifを使わず簡単に表示できる。
a = b || cの応用っぽい動き。

map(&:method)

numbers = ["1", "2", "3"]

numbers.map{ |n| n.to_i }

numbers.map(&:to_i)

mapeachのような配列のメソッドの書き方。(&:メソッド名)で中身のオブジェクトそれぞれにメソッドを実行できる。
&演算子と、procと、Object#method について理解しなおす

if array.each do end.empty? end

if articles.empty?
  articles.each do |article|
    # ループ処理
  end
else
   # articlesに何も入っていない時の処理
end
if articles.each do |article|
  # ループ処理
end.empty?
  # articlesに何も入っていない時の処理
end

articles ... endを固まりとして、empty?メソッドで判定している。

これの是非については、rubyでeachをする際に、中身が空であれば別の処理をさせるのコメント欄で議論がされていました。あまりわかりやすい書き方ではないようです。

配列をフィルタリングする

元々nilが入っている配列からnilを除く場合はcompactを使う。

[1, 2, nil, 4, nil, 6].compact
=> [1, 2, 4, 6]

Array#compact (Ruby 2.7.0 リファレンスマニュアル)

Enumerable#select/filter

filterはselectのエイリアス(Ruby2.6から)

tags.select{ |tag| tags.reload.include?(tag) }

条件などでフィルタリングしたい場合は、selectを使うと、{}内の条件に合うものだけを格納した配列として返す。
Enumerable#filter (Ruby 2.7.0 リファレンスマニュアル)

Enumerable#filter_map(Ruby2.7から)

条件でフィルタリングした後で配列の中身に編集を加える場合はこのように書く。
(例は不適かもしれません)

tags.map do |tag|
  tag.user if tags.reload.include?(tag)
end.compact

Ruby2.7ではfilter_mapというメソッドが追加されたので、このように書くことができる。

tags.filter_map { |tag| tag.user if tags.reload.include?(tag) }

Module: Enumerable (Ruby 2.7.0)
Ruby 2.7がEnumerable#filter_mapを追加| Saelounブログ

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
4
Help us understand the problem. What are the problem?