2010-01-08 15 views

Respuesta

11

Para evitar este cambio a partir de piezas independientes por completo de ruptura de su programa (como otras gemas de rubí que está utilizando), crea una clase separada para su picadillo insensibles.

class HashClod < Hash 
    def [](key) 
    super _insensitive(key) 
    end 

    def []=(key, value) 
    super _insensitive(key), value 
    end 

    # Keeping it DRY. 
    protected 

    def _insensitive(key) 
    key.respond_to?(:upcase) ? key.upcase : key 
    end 
end 

you_insensitive = HashClod.new 

you_insensitive['clod'] = 1 
puts you_insensitive['cLoD'] # => 1 

you_insensitive['CLod'] = 5 
puts you_insensitive['clod'] # => 5 

Después de anular las funciones de asignación y recuperación, es casi un pastel. Crear un reemplazo completo para Hash requeriría ser más meticuloso a la hora de manejar los alias y otras funciones (por ejemplo, #has_key? Y #store) necesarias para una implementación completa. El patrón anterior se puede extender fácilmente a todos estos métodos relacionados.

+3

Me doy cuenta de que esto fue hace casi cinco años, pero "insensible terrón" me hizo reír a carcajadas. Bravo. –

+0

Advertencia: Esto tampoco funciona con hashes anidados. – jrg

+0

@James Hasta donde yo sé, si crea cada nivel de anidamiento como HashClod. Si crea hashes predeterminados, por supuesto, fallará. –

1

¿Alguna razón para no solo usar string # upcase?

h = Hash.new 

h["HELLO"] = 7 

puts h["hello".upcase] 

Si insiste en la modificación de hash, se puede hacer algo como lo siguiente

class Hash 
alias :oldIndexer :[] 

def [](val) 
    if val.respond_to? :upcase then oldIndexer(val.upcase) else oldIndexer(val) end 
end 
end 

Desde que se crió, también puede hacerlo a realizar el ajuste entre mayúsculas y minúsculas:

class Hash 
alias :oldSetter :[]= 
def []=(key, value) 
    if key.respond_to? :upcase then oldSetter(key.upcase, value) else oldSetter(key, value) end 
end 
end 

También recomiendo hacerlo usando module_eval.

+0

h [ "Hola "] = 7? –

+0

Se hizo la suposición de que el ajuste del hash se estaba realizando con mayúsculas. Puede ampliar fácilmente lo anterior a [] = – mletterle

1

En general, diría que este es un mal plan; Sin embargo, si yo fuera usted, me gustaría crear una subclase de hash que anula el método []:

class SpecialHash < Hash 
    def [](search) 
    # Do special code here 
    end 
end 
+1

Solo para agregar un poco más a esta respuesta: anule # [] = para llamar a #downcase en las claves que recibe y luego en # [] puede llamar a self.get (buscar. en el fondo). –

+0

@Federico Builes - +1 - Gracias por el bit adicional :-) –

1
require 'test/unit' 
class TestCaseIndifferentHash < Test::Unit::TestCase 
    def test_that_the_hash_matches_keys_case_indifferent 
    def (hsh = {}).[](key) super(key.upcase) end 

    hsh['HELLO'] = 7 
    assert_equal 7, hsh['hello'] 
    end 
end 
+1

Esto, por supuesto, explotará si la clave no implementa un método llamado upcase ... – mletterle

14

Si realmente desea ignorar el caso en ambas direcciones y manejar todos los métodos Hash como #has_key?, #fetch , #values_at, #delete, etc., necesitarás trabajar un poco si quieres construir esto desde cero, pero si creas una nueva clase que se extiende desde la clase ActiveSupport::HashWithIndifferentAccess, deberías ser capaz de hacerlo bastante fácilmente como :

require "active_support/hash_with_indifferent_access" 

class CaseInsensitiveHash < HashWithIndifferentAccess 
    # This method shouldn't need an override, but my tests say otherwise. 
    def [](key) 
    super convert_key(key) 
    end 

    protected 

    def convert_key(key) 
    key.respond_to?(:downcase) ? key.downcase : key 
    end 
end 

Aquí hay algunos comportamientos ejemplo:

h = CaseInsensitiveHash.new 
h["HELLO"] = 7 
h.fetch("HELLO")    # => 7 
h.fetch("hello")    # => 7 
h["HELLO"]      # => 7 
h["hello"]      # => 7 
h.has_key?("hello")    # => true 
h.values_at("hello", "HELLO") # => [7, 7] 
h.delete("hello")    # => 7 
h["HELLO"]      # => nil 
+1

¡Claro, no sabía de eso! –

+3

Advertencia, este enfoque no funciona con hashes anidados, ya que su subclase no se usará –

0

Si bien el enfoque de Ryan McGeary funciona muy bien y es casi seguro que es la forma correcta de hacerlo, hay un error que no he podido discernir, que rompe el método Hash[].

Por ejemplo:

r = CaseInsensitiveHash['ASDF', 1, 'QWER', 2] 
=> {"ASDF"=>1, "QWER"=>2} 
r['ASDF'] 
=> nil 
ap r 
{ 
    "ASDF" => nil, 
    "QWER" => nil 
} 
=> {"ASDF"=>1, "QWER"=>2} 

Aunque no he sido capaz de encontrar o corregir la causa subyacente del error, este truco hace mejorar el problema:

r = CaseInsensitiveHash.new(Hash['ASDF', 1, 'QWER', 2]) 
=> {"asdf"=>1, "qwer"=>2} 
r['ASDF'] 
=> 1 
ap r 
{ 
    "asdf" => 1, 
    "qwer" => 2 
} 
=> {"asdf"=>1, "qwer"=>2} 
+0

Como seguimiento, lo que realmente necesitaba era un hash ignorante de mayúsculas/preservación de mayúsculas y minúsculas, pero la creación de subventa HashWithIndifferentAccess termina cortando el caso de las claves originales (sin mencionar el error de método [] anterior), así que me di por vencido y En lugar de eso, parcheó un método fetch_indiferentemente en Hash. – rantler

Cuestiones relacionadas