Yanonoblog!

こつこつと

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には、配列内の各要素(ハッシュ)が順に代入されます。

一回目の処理のポイントです。

  • 一つ目のハッシュのcolorキーのバリューである”red”を取得
  • ”red”をキーとするハッシュにブロックの引数であるハッシュが追加される
  • 上記を繰り返し処理で順番にcolorキーを基準としたハッシュの配列を作成し、colorキーが同じ場合は配列に追加されることでグルーピングされる
  • これによりブロックの戻り値が同じ要素同士がグループ化されます。

    ここでは、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を実現する例です。

    image

    都道府県のハッシュを利用してフォームを作成する際に県名に合わせて見出しとして地方名を列挙する際に非常に便利です。

    適当ですが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です♪

    https://runteq.jp/r/ohtFwbjW

    こちらのリンクを経由すると1万円引きになります。

    RUNTEQを通じて開発学習の末、受託開発企業をご紹介いただき、現在も双方とご縁があります。

    もし、興味がありましたらお気軽にコメントか、TwitterのDMでお声掛けください。

    https://twitter.com/outputky