这是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"}]
答案 0 :(得分:5)
要使任何对象使用Array #unitq,你必须覆盖两个方法: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"]
答案 1 :(得分: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"}]
答案 2 :(得分:2)
问题是Hash#hash
和Hash#eql?
都在Ruby 1.8.6中给出了伪造的结果。这是我一直愿意执行的非常罕见的猴子补丁之一,因为这个bug严重破坏了很多代码 - 特别是记忆功能。小心猴子补丁,你不要覆盖不破坏的行为。
所以:
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启动时引发错误。
答案 3 :(得分:1)
这个怎么样?
h={}
[{"three"=>"3"}, {"three"=>"4"}, {"three"=>"3"}].select {|e| need=!h.key?(e) ; h[e]=1 ; need}
#=> [{"three"=>"3"}, {"three"=>"4"}]
答案 4 :(得分: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中失败。
答案 5 :(得分: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"}]
这样的事情怎么样?只需通过块的hashq_by哈希值或哈希键。