[ruby] 配列内の要素に重複がないか検証するメソッドを実装(Array#uniq?)

概要

Rubyにて、配列の要素が重複しているかどうかを検証したい。例えば[1,2,3]ならtrueを、[1,2,3,2]ならfalseを戻すように。

Arrayの標準メソッドにそれらしいものがあると思っていたが、どうやら内容なので自前で作ることに。

前提

ruby2.2.2で動作確認

普通にメソッドを実装する

def uniq?(array)
  uniq_check = {}
  array.each do |v|
    return false if uniq_check.key? v
    uniq_check[v] = true
  end
  return true
end

array.uniq.size == array.size とかでも良かったけど、パフォーマンス的にこっちのほうが良さげ?

配列を引数にして利用する。

[2] pry(main)> uniq?([1,2,3])
=> true
[3] pry(main)> uniq?([1,2,3,2])
=> false

Arrayクラスにメソッドを追加する

前項の普通にメソッドを実装するのも良いけど、使い方がRubyっぽくないので、Arrayクラスにメソッドを追加する形で再実装する。

class Array
  ##
  # 要素が全てユニークであるかを評価
  # [1,2,3].uniq?   - true
  # [1,2,3,2].uniq? - false
  def uniq?
    uniq_check = {}
    each do |v|
      return false if uniq_check.key? v
      uniq_check[v] = true
    end
    return true
  end
end

Arrayのメソッドとしてuniq?を呼べるようになる

[2] pry(main)> [1,2,3].uniq?
=> true
[3] pry(main)> [1,2,3,2].uniq?
=> false

オマケ 重複してる要素を取得する

あわせて、[1,2,3,2,3]なら[2,3]を、[1,1,1,1]なら[1]を取得するようなメソッドも欲しかったのでまとめて実装した。

class Array
  def duplicates
    uniq_check = {}
    dups = {}
    each do |v|
      dups[v] = true if uniq_check.key? v
      uniq_check[v] = true
    end
    dups.keys
  end
end

良い感じに抜き出せた

[2] pry(main)> [1,2,3,2,3].duplicates
=> [2, 3]
[3] pry(main)> [1,1,1,1,1].duplicates
=> [1]

オマケ rspecによる単体テスト

Array#uniq?とArray#duplicatesのrspecによる単体テストも書いた。

describe Array do
  describe 'uniq?' do
    subject { array.uniq? }

    context '[1, 2, 3]' do
      let(:array) { [1, 2, 3] }
      it { is_expected.to be true }
    end
    context '[1, 2, 3, 2]' do
      let(:array) { [1, 2, 3, 2] }
      it { is_expected.to be false }
    end
    context '[]' do
      let(:array) { [] }
      it { is_expected.to be true }
    end
  end

  describe 'duplicates' do
    subject { array.duplicates }

    context '[1, 2, 3]' do
      let(:array) { [1, 2, 3] }
      it { is_expected.to eq [] }
    end
    context '[1, 2, 3, 2, 3]' do
      let(:array) { [1, 2, 3, 2, 3] }
      it { is_expected.to eq [2, 3] }
    end
    context '[1,2,1]' do
      let(:array) { [1, 2, 1] }
      it { is_expected.to eq [1] }
    end
    context '[1, 1, 1, 1, 1]' do
      let(:array) { [1, 1, 1, 1, 1] }
      it { is_expected.to eq [1] }
    end
  end
end

テスト結果

Array
  uniq?
    [1, 2, 3]
      should equal true
    [1, 2, 3, 2]
      should equal false
    []
      should equal true

  duplicates
    [1, 2, 3]
      should eq []
    [1, 2, 3, 2, 3]
      should eq [2, 3]
    [1,2,1]
      should eq [1]
    [1, 1, 1, 1, 1]
      should eq [1]

Finished in 0.01665 seconds (files took 4.48 seconds to load)
7 examples, 0 failures

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です