2010-07-26 13 views
23

Soy una persona Perl y he hecho Hashes así durante un tiempo:Creación de hash anidada con un liner en Ruby? (Vengo de Perl)

 
my %date; 

#Assume the scalars are called with 'my' earlier 

$date{$month}{$day}{$hours}{$min}{$sec}++ 

Ahora estoy aprendiendo Ruby y me he encontrado hasta ahora que el uso de este árbol es la forma de hacer muchas llaves y un valor. ¿Hay alguna forma de usar el formato simple que uso con Perl usando una línea?

 
@date = {                                          
     month => {                                         
      day => {                                         
      hours => {                                        
       min => {                                        
       sec => 1                                       
       }                                          
      }                                          
      }                                           
     }                                                                                      
     }     
+0

No sé una manera de hacerlo fuera de la caja. Probablemente podrías extender la clase Array o Hash con un método que tomara algo como '[month, day, hours, min, sec, 1]' como argumento y lo convirtieras en ese tipo de hash, si es algo que piensa que es útil. – Karl

+0

Me parece que la gente de Ruby utiliza una forma diferente de almacenar información dentro de un script en ejecución. Utilizo hashes como crazy en Perl para mantener mis datos únicos y categorizados. ¿Hay un mejor método para almacenar datos en este caso? Estoy usando este fragmento en un script de análisis de registro y es posible que se agreguen más claves para diferentes entradas de registro que necesito monitorear y mantener categorizadas. – BioDevMike

+1

Otras preguntas de "autocifra hash en Ruby": http://stackoverflow.com/questions/170223/hashes-of-hashes-idiom-in-ruby http://stackoverflow.com/questions/3148747/is-auto-initialization -de-multi-dimensional-hash-array-posible-en-ruby-as-it-is y http://stackoverflow.com/questions/3172342/how-to-handle-combination-for-auto-vivifying-hash -in-ruby –

Respuesta

41

Lamentablemente, no es no simple, forma práctica. Un equivalente de Ruby sería una fea, fea bestia como:

((((@date[month] ||= {})[day] ||= {})[hours] ||= {})[min] ||= {})[sec] = 1 

Hay una manera de asignar valores por defecto por faltar las llaves en los hashes, sin embargo:

@date = Hash.new { |hash, key| hash[key] = {} } 

# @date[:month] is set to a new, empty hash because the key is missing. 
@date[:month][:day] = 1 

Desafortunadamente esto no funciona de forma recursiva .

... a menos que lo cree usted mismo; ¡Hurra por Ruby!

class Hash 
    def self.recursive 
    new { |hash, key| hash[key] = recursive } 
    end 
end 

@date = Hash.recursive 
@date[month][day][hours][min][sec] = 1 
# @date now equals {month=>{day=>{hours=>{min=>{sec=>1}}}}} 

Tenga en cuenta, sin embargo, no se ha establecido que todos los valores son ahora {} en lugar de nil.

+0

Gracias por una gran respuesta. Una cosa más. ¿Cómo harías para almacenar los datos analizados? Yo uso hash en perl, ¿cuál es su opinión sobre el almacenamiento de datos analizados categorizados? (Mi otro comentario para mi publicación de pregunta tiene más) – BioDevMike

+0

Si necesita los datos disponibles por mes, por mes + día, por mes + día + hora, etc., entonces su enfoque está bien. Si necesita hacer algo más con eso, realmente depende de su caso de uso. Incluso podría estar bien simplemente usando una matriz como '[month, day, hours, min, sec]' o una cadena simple como tecla hash; pero no puedo decir sin saber más. – molf

+0

También encontré que podía pegarme en los símbolos fácilmente haciendo: @date [mes.to_sym] [día.a_es decir] [horas.a_sim] [min.to_sym] [seg.to_sym] = + 1 De todos modos, para pegar eso en esa función recursiva a las teclas auto sym? – BioDevMike

1

Utilización de símbolos parecían funcionar:

ree-1.8.7-2009.10 > @date = {:month =>{:day => {:hours => {:min => {:sec => 1 } } } } } 
=> {:month=>{:day=>{:hours=>{:min=>{:sec=>1}}}}} 

entonces puedo recuperar el val así:

ree-1.8.7-2009.10 > @date[:month][:day] 
=> {:hours=>{:min=>{:sec=>1}}} 
+3

No es una respuesta útil. – friedo

+0

¿De verdad? Ver arriba ... – nicholasklick

+0

Ahora que lo ha editado eliminaré mi -1 :) – friedo

1

No se ve como Ruby puede hacer autovivification desde el principio, pero puede agregar fácilmente esa funcionalidad. Una búsqueda de "autovivification rubí" en Google da:

http://t-a-w.blogspot.com/2006/07/autovivification-in-ruby.html

¿Qué contiene un ejemplo digno de cómo crear un hash que funcione de la manera que está buscando.

ruby hash autovivification (facets) también puede ser útil.

+0

Gracias por esos enlaces. Consigues el segundo lugar. He aprendido una nueva palabra "autovida". – BioDevMike

1

Puede usar la Facets gema Hash.autonew para hacer lo mismo que la función recursive dada en la respuesta de Molf.

13

Aquí hay un par de opciones similares a la respuesta dada por @molf pero sin el parche de mono.

El uso de un método de fábrica:

def hash_tree 
    Hash.new do |hash, key| 
     hash[key] = hash_tree 
    end 
    end 

    @date = hash_tree 
    @date[month][day][hours][min][sec] = 1 

Con una clase personalizada:

class HashTree < Hash 
    def initialize 
     super do |hash, key| 
     hash[key] = HashTree.new 
     end 
    end 
    end 

    @date = HashTree.new 
    @date[month][day][hours][min][sec] = 1 
16
->f{f[f]}[->f{Hash.new{|h,k|h[k]=f[f]}}] 

Obviamente.

+9

Ideal para quienes vienen de perl – artemave

+0

Me tomó 20 minutos entenderlo. No podré reproducirlo. Pero es genial. – artemave

+1

Por favor, agregue explicaciones. – artemave

11

En comparación con la expresión lambda dada anteriormente, esto es más simple y también en una línea:

Hash.new {|h,k| h[k] = Hash.new(&h.default_proc) }