2011-07-12 14 views
8

estoy jugando con Ruby y básicamente tengo¿Cómo se accede a los elementos anidados de un hash con una sola clave de cadena?

@trans = { :links => { 
    :quick_notes => "aaaaaaa" 
    } 
} 

Quiero llamar algo así como

def t 
    #...something 
end 
t('links.quick_notes') 

acceder a

trans[:links][:quick_notes] 

básicamente estoy tratando de lograr el la misma funcionalidad que cuando se utilizan las internacionalizaciones

I18n.t('something.other.foo') 

hasta la fecha se me ocurrió con este enfoque

def t(key) 
    a='' 
    key.to_s.split('.').each{|key| a+="[:#{key}]" } 
    #now a == "[:links][:quick_notes]" 
    #but I cant figure out how can I call it on @trans variable 

end 

t('links.quick_notes') 

¿Alguna idea? Gracias

+2

Este es un ejercicio divertido de recursividad. –

Respuesta

13

Se puede llegar con inject:

def t(key) 
    key.to_s.split('.').inject(@trans) { |h, k| h[k.to_sym] } 
end 

La comprobación de errores y "no hay tal entrada" comprobación se dejan como ejercicio.

+1

+1 Por ser muy sucinto. –

+5

Aún más lindo: 'key.to_s.split ('.'). Map (&: to_sym) .inject (@trans,: [])' –

+0

@ Marc-André: Es una idea interesante. Mi versión bloque atrae mis ojos a lo importante y hace que sea fácil "asimilar a simple vista" para mí. En su mayoría, es una cuestión de preferencia y de lo que estás acostumbrado. –

1

Por lo que vale, aquí hay un código manky que muestra el use of recursion.

def getKeyChain (obj, parts) 
    if !obj || parts.size == 0 
    # this is the base case 
    obj 
    else 
    # this is the recursive case 
    key = parts[0] 
    if key.match(/^:/) 
     key = key[1..key.size].to_sym 
    end 
    # each time recursing, pass a new state. 
    # in this case, the value for the "key" and the remaining parts 
    # that need resolving 
    getKeyChain(obj[key], parts[1..parts.size]) 
    end 
end 

def getCompoundKey (obj, compound) 
    # helper makes it easier to call while not making the 
    # recursive function more complex. 
    getKeyChain(obj, compound.split(".")) 
end 

h0 = {:x => "hello"} 
h1 = {"a" => {:b => "world"}} 
puts getCompoundKey(h0, ":x") # => hello 
puts getCompoundKey(h1, "a.:b") # => world 

Se pueden realizar muchas mejoras ... "usar a riesgo propio".

Happy coding.

0

Otra adición - Si tiene JSON o un hash que incluye matrices, así como los hashes - tal vez algo como esto:

@trans = { :links => [ { 
    :quick_notes => "aaaaaaa" 
    }, 
    { 
    :quick_notes => "bbbbbbb" 
    } ], 
    :page => 1 
} 

Puede incluir una prueba de posición en una matriz, se puede hacer este tipo de la cosa

def t(key) 
    key.split('.').inject(@trans) { |h,v| h.send('[]',v.to_i.to_s.eql?(v) ? v.to_i : v) } 
end 

Esto permitirá pasar una llave como esta

t('links.1.quick_notes') 

y obtener la respuesta 'bbbbbb'

1

Si usted está buscando algo que interpola %{variable}, incluyendo soporte para matrices, me han estado usando esto:

def interpolate(string, hash) 
    string.gsub(/%\{([^\}]*)\}/).each { |match| match[/^%{(.*)}$/, 1].split('.').inject(hash) { |h, k| h[(k.to_s == k.to_i.to_s) ? k.to_i : k.to_sym] } } 
end 
0

Desde Rubí 2.3 Hash tiene un dig método. la verificación de "no dicha entrada" está incorporada: si falta alguna de las claves, la expresión completa devuelve nil. Uso:

@trans = { :links => { 
    :quick_notes => "aaaaaaa" 
    } 
} 

def t (str) 
    syms = str.split(".").map(&:to_sym) 
    @trans.dig(*syms) 
end 
p t('links.quick_notes') # => "aaaaaaa" 
Cuestiones relacionadas