扩展 uniq 方法

发布于 2024-08-07 07:44:16 字数 498 浏览 11 评论 0原文

这是 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"}]

This is Ruby 1.8 Question:

We all know how to use Array#uniq :

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

However I'm wondering if we can monkey patch it in a way to work with complex objects. The current behavior is like this:

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

The requested one is:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

月竹挽风 2024-08-14 07:44:17

我自己也多次遇到过这种情况。 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 中失败。

I've run into this myself many times. Hash equality in Ruby 1.8.6 is broken:

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

Passes in Ruby 1.9 and Ruby 1.8.7, fails in Ruby 1.8.6.

迷你仙 2024-08-14 07:44:17
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 。

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"}] 

How about something like that? just uniq_by the hash value or hash key via the block.

要使 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','[email protected]'),User.new('E. Smith','[email protected]')].uniq 
=> [#<User:0x1015a97e8 @name="Erin Smith", @email="[email protected]"]

To make Array#uniq work for any object you must override two methods: hash and eql?

All objects have a hash method which calculates the hash value for that object, so for two objects to be equal their values when hashed must also be equal.

Example--a user is unique when their email address is unique:

class User
  attr_accessor :name,:email

  def hash
    @email.hash
  end

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

>> [User.new('Erin Smith','[email protected]'),User.new('E. Smith','[email protected]')].uniq 
=> [#<User:0x1015a97e8 @name="Erin Smith", @email="[email protected]"]
全部不再 2024-08-14 07:44:16

它在 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"}]

It already works for me in 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"}]
水水月牙 2024-08-14 07:44:16

问题是 Hash#hashHash#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 开始时引发错误即可。

The problem is that Hash#hash and Hash#eql? both give bogus results in Ruby 1.8.6. This is one of the very rare monkey patches I've been willing to perform, because this bug seriously breaks a lot of code — in particular memoizing functions. Just be careful with monkey patches that you don't override non-broken behavior.

So:

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

But if you're doing something where you control the deploy environment, just raise an error if the app gets started with 1.8.6.

蓝海 2024-08-14 07:44:16

这个怎么样?

h={}
[{"three"=>"3"}, {"three"=>"4"}, {"three"=>"3"}].select {|e| need=!h.key?(e) ; h[e]=1 ; need} 
#=> [{"three"=>"3"}, {"three"=>"4"}]

How about this?

h={}
[{"three"=>"3"}, {"three"=>"4"}, {"three"=>"3"}].select {|e| need=!h.key?(e) ; h[e]=1 ; need} 
#=> [{"three"=>"3"}, {"three"=>"4"}]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文