Rails - group_byメソッドの仕組みと活用例
背景
バックエンドの実装を行った際にgroup_byメソッドについて色々調べたため内容を書き留めておきたいと思いました。
本記事では、group_by
メソッドの活用方法を具体的なViewを作りつつ解説していきます。
情報に関しては適当にフェイクを入れていますので、ご注意ください
概要
RubyのEnumerableモジュールに含まれるgroup_byメソッドは、配列内の要素を指定した条件でグループ化することができるメソッドです。
group_by
メソッドは配列、ハッシュ、範囲オブジェクトなど、Enumerableモジュールをincludeしているクラスのインスタンス全てで使用可能です。
例として以下の果物の名前と色が格納されたhashの配列があります。
fruits = [ { name: "apple", color: "red" }, { name: "banana", color: "yellow" }, { name: "grape", color: "purple" }, { name: "orange", color: "orange" }, { name: "watermelon", color: "green" }, { name: "strawberry", color: "red" } ]
解説 - group_byでグループ化
この配列をgroup_byメソッド
を使ってcolorキーでグループ化
します。
fruits_by_color = fruits.group_by { |fruit| fruit[:color] }
上記のコードでは、ブロックに渡される引数fruitには、配列内の各要素(ハッシュ)が順に代入
されます。
一回目の処理のポイントです。
これによりブロックの戻り値が同じ要素同士がグループ化
されます。
ここでは、fruit[:color]の値でグループ化しているため、同じ色の果物が一つの配列にまとめられます。
group_byで得られる結果値
上記の結果、以下の形式になります。
{ "red": [ { name: "apple", color: "red" }, { name: "strawberry", color: "red" } ], "yellow": [ { name: "banana", color: "yellow" } ], "purple": [ { name: "grape", color: "purple" } ], "orange": [ { name: "orange", color: "orange" } ], "green": [ { name: "watermelon", color: "green" } ] }
グループ化された結果は、以下のように表示することができます。
fruits_by_color.each do |color, fruits| puts "#{color}: #{fruits.map { |fruit| fruit[:name] }.join(', ')}" end
上記のコードでは、グループ化された各要素のキー(color)と、それに対応する値(同じcolorの果物の配列)を、ブロック変数colorとfruitsに代入しています。
mapメソッドで各果物の名前の配列を取得し、joinメソッドでチェーンしてカンマ区切りの文字列に変換しています。
出力結果は下のようになります。
red: apple, strawberry yellow: banana purple: grape orange: orange green: watermelon
group_byメソッドは、グループ化したい条件が複数ある場合にも使えます。以下は、colorとsizeの両方でグループ化する例です。
fruits_by_color_and_size = fruits.group_by { |fruit| [fruit[:color], fruit[:size]] }
出力結果は以下のようになります。
[ [["red", "small"]], [], [["red", "medium"]], [ { name: "banana", color: "yellow", size: "small" } ], [["purple", "medium"]], [ { name: "orange", color: "orange", size: "large" } ], [["green", "large"]], [ { name: "strawberry", color: "red", size: "medium" }, { name: "watermelon", color: "green", size: "large" } ] ]
実用例
今回のケースを利用して都道府県を複数選択出来るUIを作成します。
実現するUI
このようなUIを実現する例です。
都道府県のハッシュを利用してフォームを作成する際に県名に合わせて見出しとして地方名を列挙する際に非常に便利です。
適当ですがcontrollerでこのようなRubyのコードを書きます。
実際はデータベースから取り出したレコードを利用するとは思いますが今回はベタ書きです。
def index @prefectures = [ { name: "北海道", region: "北海道" }, { name: "青森県", region: "東北" }, { name: "岩手県", region: "東北" }, { name: "宮城県", region: "東北" }, { name: "秋田県", region: "東北" }, { name: "山形県", region: "東北" }, { name: "福島県", region: "東北" }, { name: "茨城県", region: "関東" }, { name: "栃木県", region: "関東" }, { name: "群馬県", region: "関東" }, { name: "埼玉県", region: "関東" }, { name: "千葉県", region: "関東" }, { name: "東京都", region: "関東" }, { name: "神奈川県", region: "関東" }, { name: "新潟県", region: "中部" }, { name: "富山県", region: "中部" }, { name: "石川県", region: "中部" }, { name: "福井県", region: "中部" }, { name: "山梨県", region: "中部" }, { name: "長野県", region: "中部" }, { name: "岐阜県", region: "中部" }, { name: "静岡県", region: "中部" }, { name: "愛知県", region: "中部" }, { name: "三重県", region: "中部" }, { name: "滋賀県", region: "近畿" }, { name: "京都府", region: "近畿" }, { name: "大阪府", region: "近畿" }, { name: "兵庫県", region: "近畿" }, { name: "奈良県", region: "近畿" }, { name: "和歌山県", region: "近畿" }, { name: "鳥取県", region: "中国" }, { name: "島根県", region: "中国" }, { name: "岡山県", region: "中国" }, { name: "広島県", region: "中国" }, { name: "山口県", region: "中国" }, { name: "徳島県", region: "四国" }, { name: "香川県", region: "四国" }, { name: "愛媛県", region: "四国" }, { name: "高知県", region: "四国" }, { name: "福岡県", region: "九州" }, { name: "佐賀県", region: "九州" }, { name: "長崎県", region: "九州" }, { name: "熊本県", region: "九州" }, { name: "大分県", region: "九州" }, { name: "宮崎県", region: "九州" }, { name: "鹿児島県", region: "九州" }, { name: "沖縄県", region: "九州" } ] @prefecture_by_region = @prefectures.group_by { |prefecture| prefecture[:region] } end
上記ではgroup_byを用いて地方名であるregionのグループを作成してインスタンス変数に代入しています。
あとは以下のようにeach文を書いてデザインを付け加えてあげると完成です!
<div class="m-4"> <h5 class="border-bottom border-3 pb-1 fw-bold"> 県名を選択する </h5> <div class="row my-2"> <% @prefecture_by_region.each do |region, prefecture| %> <div class="col-md-2 menu-group m-3"> <h6 class="border-bottom border-gray pb-1 fw-bold" style="border-left:8px solid #87cefa; padding:2px 8px; background-color: white;"> <%= region %> </h6> <div class="m-2"> <% prefecture.each do |prefecture| %> <%= check_box_tag "prefectures[]", prefecture[:name], false, style: 'transform: scale(1.3);', class: 'checks m-2' %> <%= label_tag prefecture[:name], prefecture[:name], style: 'transform: scale(1.1);', class: 'ml-2 m-1' %><br> <% end %> </div> </div> <% end %> </div> </div>
これにもう少し地図の画像デザインなどを加えたりボックス背景を地方ごとにつけてあげるとUIとしては様になってきますね。
メソッド自体も活用できると便利なのではないでしょうか。
おわりに
コメント
本記事の内容は以上になります!
参考になったり学びのきっかけになりますと幸いです。
間違いがありましたら修正いたしますので、ご指摘ください。
興味があれば他の記事も更新していきますので是非ご覧になってください♪
プログラミングスクールのご紹介 (卒業生より)
お世話になったプログラミングスクールであるRUNTEQです♪
こちらのリンクを経由すると1万円引きになります。
RUNTEQを通じて開発学習の末、受託開発企業をご紹介いただき、現在も双方とご縁があります。
もし、興味がありましたらお気軽にコメントか、TwitterのDMでお声掛けください。