言語
日本語
English

Caution

お使いのブラウザはJavaScriptが無効になっております。
当サイトでは検索などの処理にJavaScriptを使用しています。
より快適にご利用頂くため、JavaScriptを有効にしたうえで当サイトを閲覧することをお勧めいたします。

Ruby辞典

  1. トップページ
  2. Ruby辞典
  3. ハッシュ.filter_map / flat_map / reduce

ハッシュ.filter_map / flat_map / reduce

対応: Ruby 2.7(2019)

ハッシュに対してフィルタリングと変換を同時に行う『filter_map』、入れ子の結果を平坦化する『flat_map』、集約処理の『reduce』(『inject』)を解説します。

構文

# 条件を満たす要素だけ変換した配列を返します(Ruby 2.7以降)。
ハッシュ.filter_map { |キー, 値| 変換後の値 または nil/false }

# ブロックの結果を1段階フラットにした配列を返します。
ハッシュ.flat_map { |キー, 値| 配列 }

# 各ペアを集約して単一の値を返します。
ハッシュ.reduce(初期値) { |累積値, (キー, 値)| 処理 }
ハッシュ.inject(初期値) { |累積値, (キー, 値)| 処理 }

# グループ分けした結果をハッシュで返します。
ハッシュ.group_by { |キー, 値| グループキー }

メソッド一覧

メソッド概要
filter_map { |k, v| ... }ブロックが『nil』か『false』以外の値を返した場合のみ、その値を結果の配列に含めます。
flat_map { |k, v| ... }ブロックが返す配列を結合して1つの配列にします。配列を返さない場合は通常の『map』と同じです。
reduce(init) { |acc, (k, v)| ... }初期値から始め、各ペアを累積処理して最終的な値を返します。『inject』も同義です。
min_by { |k, v| ... }ブロックの戻り値が最小のペアを『[キー, 値]』の形式で返します。
max_by { |k, v| ... }ブロックの戻り値が最大のペアを返します。
sort_by { |k, v| ... }ブロックの戻り値を基準に並べ替えた配列を返します。
group_by { |k, v| ... }ブロックの戻り値でグループ分けした結果をハッシュで返します。

サンプルコード

sample_hash_filter_map_flat_map.rb
enforcers = { kogami: 5, ginoza: 0, masaoka: 8, karanomori: 0, tsunemori: 3 }

# filter_map:稼働中のメンバー名だけを取得(select + map を一度に)
active = enforcers.filter_map { |member, score| member.to_s if score > 0 }
puts active.inspect # ["kogami", "masaoka", "tsunemori"]

# flat_map:各ペアを複数の文字列に展開
labels = enforcers.flat_map { |member, score| [member.to_s, score.to_s] }
puts labels.inspect
# ["kogami", "5", "ginoza", "0", "masaoka", "8", "karanomori", "0", "tsunemori", "3"]

# reduce:合計スコアを計算
total = enforcers.reduce(0) { |sum, (_, score)| sum + score }
puts "合計スコア: #{total}" # 合計スコア: 16

# reduce:スコアありだけのハッシュを構築
available = enforcers.reduce({}) do |hash, (member, score)|
  hash[member] = score if score > 0
  hash
end
puts available.inspect # {:kogami=>5, :masaoka=>8, :tsunemori=>3}

# min_by / max_by:最低・最高スコアのメンバーを特定
puts enforcers.min_by { |_, score| score }.inspect # [:ginoza, 0]
puts enforcers.max_by { |_, score| score }.inspect # [:masaoka, 8]

# sort_by:スコアで並べ替え
sorted = enforcers.sort_by { |_, score| score }
sorted.each { |member, score| puts "#{member}: #{score}" }

実行すると次のように出力されます。

ruby hash_filter_map_flat_map.rb
["kogami", "masaoka", "tsunemori"]
["kogami", "5", "ginoza", "0", "masaoka", "8", "karanomori", "0", "tsunemori", "3"]
合計スコア: 16
{:kogami=>5, :masaoka=>8, :tsunemori=>3}
[:ginoza, 0]
[:masaoka, 8]
ginoza: 0
karanomori: 0
tsunemori: 3
kogami: 5
masaoka: 8

概要

『filter_map』はRuby 2.7で追加されたメソッドで、フィルタリングと変換を1回のイテレーションで行えます。『select { }.map { }』の2段階処理を1つにまとめられるため、パフォーマンスが向上し、コードも短くなります。

『reduce』(『inject』)は強力ですが可読性が下がりがちです。合計・最大・最小を求めるだけなら、より意図が明確な『sum』『min_by』『max_by』を優先しましょう。ハッシュを構築するにはeach_with_objectの方が読みやすいケースも多いです。

『sort_by』はハッシュに対して使うと配列(ペアの配列)を返します。結果をハッシュに戻すには末尾に『to_h』を追加します。複数の条件でソートする場合は、ブロック内で配列を返す(例:『sort_by { |k, v| [v, k.to_s] }』)テクニックが使えます。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。