Расширение уникального метода

Это 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"}]
13.10.2009 04:19:00
6 ОТВЕТОВ
РЕШЕНИЕ

У меня это уже работает в 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"}]
4
13.10.2009 04:40:51

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

h={}
[{"three"=>"3"}, {"three"=>"4"}, {"three"=>"3"}].select {|e| need=!h.key?(e) ; h[e]=1 ; need} 
#=> [{"three"=>"3"}, {"three"=>"4"}]
1
13.10.2009 07:27:56
Это очень индивидуальный подход, я хотел общее решение, которое учитывает равенство объектов.
khelll 13.10.2009 07:51:05

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

Проблема заключается в том, что Hash#hashи Hash#eql?оба дают фиктивные результаты в Руби 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.

2
13.10.2009 15:51:38
Боб, если ты будешь делать это регулярно, я бы хотел увидеть, что ты вкладываешь в эти методы. Интересно, станет ли он хорошим кандидатом для подачи в жемчужину бэкпортов?
dkubb 22.01.2010 06:19:27
Я должен отметить, что проект backports добавил
dkubb 23.01.2010 16:22:45
Если честно, мне нравится их реализация # code goes hereлучше.
Bob Aman 24.01.2010 21:03:19

Чтобы 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"]
6
12.11.2010 21:24:04
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 хеш-значение или хеш-ключ через блок.

0
14.10.2011 21:20:16