Arrayの重複チェックをする
Arrayの重複チェックに手間取ったのでメモ。
普通に考えると、ユニークにして元のものとの差分を取ればいいやと思うのだけど、うまく行かない。
irb(main):001:0> a = [1,1,1,2,2,3] => [1, 1, 1, 2, 2, 3] irb(main):002:0> a - a.uniq => [] ※ここで[1,1,2]が返ってきて欲しい。
Array同士の差を取るときは、集合とみなして重複を削ってしまうそうな。。
http://www.ruby-lang.org/ja/man/html/Array.html#self.20-.20other
というわけで以下のコードを書いた。
def overlapped(an_array) an_hash = {} overlapped = [] an_array.each{|a| if an_hash.include?(a) overlapped << a else an_hash[a] = nil end } overlapped end
実行すると、以下になる。うまく行ったね。
irb(main):001:0> a = [1,1,1,2,2,3] => [1, 1, 1, 2, 2, 3] irb(main):002:0> overlapped a => [1, 1, 2]
もちろん、Arrayのメソッドに組み込んでしまっても良い。
a.uniqのように、a.overlappedと書けるのでこっちの方が良いな。(破壊的メソッドにするのはさすがに気持ち悪い。。)
class Array def overlapped an_hash = {} overlapped = [] self.each{|a| if an_hash.include?(a) overlapped << a else an_hash[a] = nil end } overlapped end end
うん、injectを使った方が素敵!ということに気づいたので、以下に差し替え。(2008/05/04)
class Array def overlapped an_hash = Hash.new self.inject([]){|r,i| an_hash.include?(i) ? (r << i) : (an_hash[i] = nil; r) } end end
こんな感じで使える。
irb(main):015:0> a = [1,1,1,2,2,3] => [1, 1, 1, 2, 2, 3] irb(main):016:0> a.overlapped => [1, 1, 2]
当然のことながら、uniqしたものと、overlappedしたものを足せば、元に戻る。
順番は変わってしまうので、お互いにsortしないと == にはならない事に注意。
irb(main):055:0> a.sort == (a.uniq + a.overlapped).sort => true