2

这是 Ruby 1.8 问题:

我们都知道如何使用Array#uniq

[1,2,3,1].uniq #=> [1,2,3]

但是我想知道我们是否可以通过猴子修补它来处理复杂的对象。当前的行为是这样的:

[{"three"=>"3"}, {"three"=>"4"}, {"three"=>"3"}].uniq 
#=> [{"three"=>"3"}, {"three"=>"4"}, {"three"=>"3"}]

请求的是:

[{"three"=>"3"}, {"three"=>"4"}, {"three"=>"3"}].uniq 
#=> [{"three"=>"3"}, {"three"=>"4"}]
4

6 回答 6

6

要使 Array#uniq 适用于任何对象,您必须重写两个方法:hash 和 eql?

所有对象都有一个哈希方法来计算该对象的哈希值,因此两个对象要相等,它们在哈希时的值也必须相等。

示例——当用户的电子邮件地址唯一时,用户就是唯一的:

class User
  attr_accessor :name,:email

  def hash
    @email.hash
  end

  def eql?(o)
    @email == o.email
  end
end

>> [User.new('Erin Smith','roo@example.com'),User.new('E. Smith','roo@example.com')].uniq 
=> [#<User:0x1015a97e8 @name="Erin Smith", @email="maynurd@example.com"]
于 2010-11-12T21:24:04.963 回答
4

它已经在 1.8.7 中对我有用。

1:~$ ruby -v
ruby 1.8.7 (2008-08-11 patchlevel 72) [i486-linux]
1:~$ irb -v
irb 0.9.5(05/04/13)
1:~$ irb
>> [{"three"=>"3"}, {"three"=>"4"}, {"three"=>"3"}].uniq 
=> [{"three"=>"3"}, {"three"=>"4"}]
于 2009-10-13T04:40:51.013 回答
2

问题是这Hash#hash两者Hash#eql?都在 Ruby 1.8.6 中给出了虚假的结果。这是我愿意执行的非常罕见的猴子补丁之一,因为这个错误严重破坏了很多代码——尤其是记忆功能。请注意不要覆盖未破坏行为的猴子补丁。

所以:

class Hash
  if {}.hash != {}.hash
    def hash
      # code goes here
    end
  end
  if !{}.eql?({})
    def eql?(other)
      # code goes here
    end
  end
end

但是,如果您正在做一些控制部署环境的事情,那么如果应用程序从 1.8.6 开始,就会引发错误。

于 2009-10-13T15:51:38.503 回答
1

这个怎么样?

h={}
[{"three"=>"3"}, {"three"=>"4"}, {"three"=>"3"}].select {|e| need=!h.key?(e) ; h[e]=1 ; need} 
#=> [{"three"=>"3"}, {"three"=>"4"}]
于 2009-10-13T07:27:56.717 回答
1

我自己也遇到过很多次。Ruby 1.8.6 中的哈希相等被破坏:

require 'test/unit'

class TestHashEquality < Test::Unit::TestCase
  def test_that_an_empty_Hash_is_equal_to_another_empty_Hash
    assert({}.eql?({}), 'Empty Hashes should be eql.')
  end
end

在 Ruby 1.9 和 Ruby 1.8.7 中通过,在 Ruby 1.8.6 中失败。

于 2009-10-13T15:14:46.563 回答
0
1.8.7 :039 > [{"three"=>"3"}, {"three"=>"4"}, {"three"=>"3"}].uniq {|x|x.values} 
=> [{"three"=>"3"}, {"three"=>"4"}] 
1.8.7 :040 > [{"three"=>"3"}, {"three"=>"4"}, {"three"=>"3"}].uniq {|x|x.keys}
=> [{"three"=>"3"}] 

那样的事情怎么样?只需 uniq_by 通过块的哈希值或哈希键。

于 2011-10-14T17:12:39.790 回答