Pergunta

Este é o Ruby 1.8 Pergunta:

Todos nós sabemos como usar Array#uniq :

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

No entanto, eu estou perguntando se é possível monkey patch-lo em uma maneira de trabalhar com objetos complexos.O comportamento atual é assim:

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

O pedido é:

[{"three"=>"3"}, {"three"=>"4"}, {"three"=>"3"}].uniq 
#=> [{"three"=>"3"}, {"three"=>"4"}]
Foi útil?

Solução

Já funciona para mim em 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"}]

Outras dicas

Para fazer a matriz#uniq funcionar para algum Objeto Você deve substituir dois métodos: Hash e EQL?

Todos os objetos têm um método de hash que calcula o valor de hash para esse objeto; portanto, para dois objetos serem iguais, seus valores quando hashed também devem ser iguais.

Exemplo-um usuário é único quando o endereço de e-mail é exclusivo:

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

O problema é que Hash#hash e Hash#eql? Ambos dão resultados falsos no Ruby 1.8.6. Esse é um dos manchas de macacos muito raras que eu estive disposto a executar, porque esse bug quebra seriamente muito código - em particular funções de memórias. Apenas tenha cuidado com os remendos de macacos que você não substitui o comportamento não derrubado.

Então:

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

Mas se você estiver fazendo algo em que controla o ambiente de implantação, basta levantar um erro se o aplicativo começar com 1.8.6.

Que tal agora?

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

Eu tenho que correr para isso mesmo, muitas vezes.Hash igualdade em Ruby 1.8.6 é quebrada:

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

Passa no Ruby 1.9 Ruby 1.8.7, falha em 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"}] 

Que tal algo assim? Apenas uniq_by o valor de hash ou a chave de hash através do bloco.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top