23235


特定のクラスのインスタンスのインスタンス変数が全て等しいなら
`uniq`でまとめてしまいたく調べていたのですがバージョンによって動作が違うのか何が正しいのかわかりませんでした。

こちらの回答によると  
<https://stackoverflow.com/questions/1650475/removing-identical-objects-in-
ruby>

[code]

    class Result
    
      attr_accessor :text, :notes
    
      def initialize(text = nil, notes = nil)
        self.text = text
        self.notes = notes
      end
    
      def ==(other)
        other.class == self.class &&
        other.text  == self.text
      end
      alias :eql? :==
    
    end
    
    a = Result.new("first")
    b = Result.new("first")
    c = Result.new("third")
    
    [a, b, c].uniq
    # => [a, c]
    
[/code]

にて重複分の削除を行えると解説されてるんですが、`ruby 2.2.2`を使っている私の環境では3つとも返ってきてしまいます。

[code]

    a.eql?(b) #=> true
    
[/code]

になることは確認できています。

どのようにすればオブジェクトが持つ外部からアクセス可能なインスタンス変数が全て等しい時に`uniq`でまとめることができるでしょうか？


CC BY-SA 3.0
2016-03-17T17:03:25.940
23234
2016-03-17T22:25:26.713
2017-05-23T12:38:55.250
-1
3271
question
ruby
クラスのインスタンスをuniqで処理する際の振る舞い


[Simoneさんの2009年からの回答のコード](https://stackoverflow.com/a/1650548/1167750)は少なくとも`Ruby
1.9.3`の前のバージョンには役に立ったと思いますが、`Ruby
1.9.3`以来、[Eeroさんの回答](https://stackoverflow.com/a/9415813/1167750)によると、[`hash`のメソードを定義する](https://stackoverflow.com/a/9415813/1167750)必要もあるようです。

それで、[Dominiqueさんの記事](http://belighted.com/en/blog/overriding-equals-
equals)が述べているように、`hash`のメソードを定義すると：

[code]

    class Result
    
      attr_accessor :text, :notes
    
      def initialize(text = nil, notes = nil)
        self.text = text
        self.notes = notes
      end
    
      def ==(other)
        other.class == self.class &&
        other.text  == self.text
      end
      alias :eql? :==
    
      def hash()
        return "example".hash
      end
    
    end
    
    a = Result.new("first")
    b = Result.new("first")
    c = Result.new("third")
    
    print "a: ", a, "\n"
    print "b: ", b, "\n"
    print "c: ", c, "\n"
    print "\n"
    puts [a, b, c].uniq
    print "\n"
    puts a.eql?(b)
    
[/code]

出力はこうなります：

[code]

    a: #<Result:0x007fc7f0833078>
    b: #<Result:0x007fc7f0833028>
    c: #<Result:0x007fc7f0832fd8>
    
    #<Result:0x007fc7f0833078>
    #<Result:0x007fc7f0832fd8>
    
    true
    
[/code]

追加：ご覧の通り、この例の`hash`の定義はただのストリングですが、もっと適切な定義する方法もあると思います。あと、この例のために、私は`Ruby
2.0.0`を使っています。


CC BY-SA 3.0
2016-03-17T20:39:53.790
23235
2016-03-17T21:57:55.223
2017-05-23T12:38:55.307
-1
4129
23234
answer


Array#uniqは同一性の判断に[Object#eql?](http://docs.ruby-
lang.org/ja/2.3.0/method/Object/i/eql=3f.html)を使用していますが、Object#eql?を再定義する場合は、
**Object#hashも再定義する必要があります**
。これは1.9.3以前から(昔のリファレンスマニュアルを見る限り少なくとも1.4の時から)の決まり事になっています。hashを正しく再定義せずにeql?のみ再定義した場合、eql?を使用する処理(例えばHashのkeyとして使用など)が正しく動作する保証はありません。

1.9.3以前のバージョンでhashを再定義しなくても動作していたのは、uniqの処理の内部で偶然にもhashを使用していなかっただけにすぎません。以前のバージョンでも、hashが未定義だった場合に正しく動作する保証は一切ありませんでした。

なお、hashの実装はeql?の同一性判断に使うプロパティを配列にしてhashにするのが一番単純かと思います。  
参考:
[[Ruby]自作クラスのオブジェクトをハッシュキーにするときの小技。](http://d.hatena.ne.jp/shunsuk/20100412/1271073263)

[code]

    def hash
      return [self.class, self.text].hash
    end
    
[/code]


CC BY-SA 3.0
2016-03-17T22:25:26.713
23236
2016-03-17T22:25:26.713
7347
23234
answer