その他
    ホーム 技術発信 DoRuby rubyで配列から重複する値を抽出する方法を探した際に見つけたgroup_byメソッドが便利だった
    rubyで配列から重複する値を抽出する方法を探した際に見つけたgroup_byメソッドが便利だった
     

    rubyで配列から重複する値を抽出する方法を探した際に見つけたgroup_byメソッドが便利だった

    この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

    重複を排除するときは uniq ですが、重複を取り出すメソッドはない。 直接実行するメソッドはないけれど Array#group_byを使うと簡単にできる

    参考:
    ・ Rubyで配列内の重複する値を抽出する方法
    http://kiyotakakubo.hatenablog.com/entry/20110801/1312196444
    ・ rubyのgroup_byが便利
    http://simanman.hatenablog.com/entry/2013/03/16/193552
    ・ instance method Enumerable#group_by
    https://docs.ruby-lang.org/ja/latest/method/Enumerable/i/group_by.html

    環境
    ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-darwin16]
    irb 0.9.6(09/06/30)

    実行例

      arr = [1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6] # 配列を準備
      arr.group_by{|i| i}.reject{|k,v| v.one?}.keys # 魔法を使う
     => [2, 3, 4, 5]
    

    Array#group_by
    ブロックで渡した値をキーとした配列をハッシュにする。
    arr.group_by{|i| i}
    => {1=>[1], 2=>[2, 2], 3=>[3, 3, 3], 4=>[4, 4, 4], 5=>[5, 5], 6=>[6]}

    応用

    ・ カナ付きの名称を持つレコードを最初の文字でグルーピングする例

    Something.all.to_a.group_by{ |product| product.name_kana.first}
    

    ・ 一行の文字列にいくつものデータを含んだ名前を持つデータをグルーピングする例

    arr2 = %w(
      productcd1_skucode1_100
      productcd1_skucode2_200
      productcd2_skucode3_100
      productcd3_skucode3_150
    ) # 商品コード_SKUコード_値段のようなイメージ
    arr2.group_by { |one_line_record| one_line_record.split('_').first } #商品コードで分けるみたいな
    
    arr2.group_by { |one_line_record| one_line_record.split('_').last.to_i} #値段で分けるみたいな
    

    重複する値を取り出すときの補足

    • Array#reject ブロックで渡した値が true を返す要素を除く
    • Array#one? 要素数が 1 あるかどうかを true / false で返す arr.group_by{|i| i}.reject{|k,v| v.one?} => {2=>[2, 2], 3=>[3, 3, 3], 4=>[4, 4, 4], 5=>[5, 5]}
    • Hash#keys ハッシュキーの配列を返す

    付録

    Arrayクラスに今回扱ったメソッドを追加するサンプル

    class Array
      def ununiq
        group_by{|i| i}.reject{|k,v| v.one?}.keys
      end
    end
     [1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6].ununiq