この記事はアピリッツの技術ブログ「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