Расширение uniq метода - PullRequest
       13

Расширение uniq метода

2 голосов
/ 13 октября 2009

Это 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"}]

Ответы [ 6 ]

5 голосов
/ 13 ноября 2010

Чтобы 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"]
4 голосов
/ 13 октября 2009

У меня уже работает в 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 голосов
/ 13 октября 2009

Проблема в том, что 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.

1 голос
/ 13 октября 2009

Я сам сталкивался с этим много раз. Хэш-равенство в 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.

1 голос
/ 13 октября 2009

Как насчет этого?

h={}
[{"three"=>"3"}, {"three"=>"4"}, {"three"=>"3"}].select {|e| need=!h.key?(e) ; h[e]=1 ; need} 
#=> [{"three"=>"3"}, {"three"=>"4"}]
0 голосов
/ 14 октября 2011
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 хеш-значение или хеш-ключ через блок.

...