Rubyで配列の重複しているモノを調べるには。
Rubyって配列から重複しているモノだけを知りたい時どうするか?Yahoo!知恵袋の回答はアテにならないし、OKWaveもイマイチだった。そこでマニュアルとにらめっこして考えた。
配列から重複を取り出す例。
=> a=[1, 2, 3, 4, 5, 6, 7, 8, 9, 2, 4, 6, 8] #偶数がダブりの配列 >> a.select{|e| a.index(e)!=a.rindex(e)} => [2, 4, 6, 8, 2, 4, 6, 8] >> a.select{|e| a.index(e)!=a.rindex(e)}.uniq => [2, 4, 6, 8]
rindex/indexの活用。
index(val) | 最初の形式では、val と == で等しい最初の要素の位置を返 します。 |
rindex(val) | val と == で等しい最後の要素の位 置を返します。 |
と言うわけ。後ろから探した場合と前から探した場合で見つかったIndex番号が違うとその要素は重複ってわけ。
ま、当たり前なんだけどね。
コレを使ってどの要素が重複していたか調べることが出来る。
ちなみに重複がある調べるなら
a.size - a.uniq.size
っかなぁ
2010-01-04追記
上記のやり方だと1000件以上重複があるときにかなり遅い。ループに無駄があるからなんだ。
先にuniqしておくと無駄が少ないかも
a.uniq.select{|i| a.index(i) != a.rindex(i)}
2012-06-29追記
sort/each_with_index で新しい配列を作れば出来る。ってid:Kirovさんからトラックバックを頂いた。たしかにそうなんだけど。言い訳させて下さい。このエントリを書いてる時に処理してた配列データは数GBあって、新しくソート済み配列作るとメモリ上限で即死だったんだ。しかもEnumerableがActiveRecordで扱いがめんどくさかった。
ちなみに、Ruby1.9のEnumratorの導入後ならもっと簡単に出来ますよ。
sortかつ、each_with_indexが使えると、かなり楽です
a =[1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9] a.sort! a.select.with_index{|e,i| e==a[i+1] } #=> [2, 4, 6, 8]
(当時はirbのconf.echo=falseを知らなかったので、irbを1行書くたび大量データ表示のechoがあって表示待ちで時間かかる。さらに表示データが大きすぎるとirbがメモリ上限で死んだりしてたりしてて困っていたと思う。今にして思えば。可愛い悩みだわ)
追記2013-11-20:「最後の間違ってるなぁ。 重複が3つあった時に正常に動作しない」⇐間違ってはないはず
ブクマコメントが気になったので追記
id:hisatake_iのブクマコメントが気になりました
http://b.hatena.ne.jp/hisatake_i/20130723#bookmark-28550154
確かに、重複がn個あったときに、n-1 個出てきてしまう。たしかに「間違い」かも知れない。
でも、重複の個数も知りたい時もあり、私にとっては個数複数個でも正解。
Uniq の逆をやりたい時の期待値
[1,2,3,3,3] - [1,2,3] #=> [3] または [3,3] のいずれかを期待。結果は[]
重複がn個の場合は利用場面に依るんじゃないでしょうか。