自分がほえーと思った小技などをメモしておく。
全てがベストプラクティスというわけではないので、状況に応じて使い分けたい。
(4/18追記)
コメントより、「もっと簡単な書き方がある」とご教示いただきましたので追加しました。ありがとうございました。
Safe Navigation Operator(ぼっち演算子)
articles = user.articles if user
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)
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)
map
やeach
のような配列のメソッドの書き方。(&:メソッド名)
で中身のオブジェクトそれぞれにメソッドを実行できる。
&演算子と、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ブログ