Вопрос

Это вопрос 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"}]
Это было полезно?

Решение

Это уже работает у меня в версии 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"}]

Другие советы

Чтобы заставить массив #uniq работать для Любой объект, который вы должны переопределить двумя методами:хэш и 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"]

Проблема в том, что Hash#hash и Hash#eql? оба дают поддельные результаты в Ruby 1.8.6.Это одно из очень редких исправлений monkey, которое я был готов выполнить, потому что эта ошибка серьезно нарушает множество кодов — в частности, функции запоминания.Просто будьте осторожны с патчами monkey, чтобы не переопределить неповрежденное поведение.

Итак:

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.

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top