Estendendo uniq método
-
21-09-2019 - |
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"}]
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.