2009-08-05 20 views
5

Tengo una pregunta de principiante Ruby sobre arreglos multidimensionales.¿Cómo hacer una matriz multidimensional dinámica en ruby?

Quiero ordenar las entradas por año y mes. Así que quiero crear una matriz multidimensional que contendría años -> meses -> entradas de meses

así que la matriz serían como:

2009 -> 
     08 
      -> Entry 1 
      -> Entry 2 
     09 
      -> Entry 3 
2007 -> 
     10 
      -> Entry 5 

Ahora tengo:

@years = [] 
@entries.each do |entry| 
    timeobj = Time.parse(entry.created_at.to_s) 
    year = timeobj.strftime("%Y").to_i 
    month = timeobj.strftime("%m").to_i 
    tmparr = [] 
    tmparr << {month=>entry} 
    @years.push(year) 
    @years << tmparr 
end 

pero cuando intento para iterar a través de la matriz de años, me sale: "método no definido` cada uno' para el año 2009: Fixnum"

Probamos también:

@years = [] 
@entries.each do |entry| 
    timeobj = Time.parse(entry.created_at.to_s) 
    year = timeobj.strftime("%Y").to_i 
    month = timeobj.strftime("%m").to_i 
    @years[year][month] << entry 
end 

Respuesta

11

Recibirá el error porque se empujó un FixNum (es decir, un número) en la matriz, en la línea que dice @years.push(year).

Su enfoque de utilizar matrices para empezar es un poco defectuoso; una matriz es perfecta para contener una lista ordenada de elementos. En su caso, tiene un mapeo de claves a valores, que es perfecto para un Hash.

En el primer nivel, las claves son años, los valores son valores hash. Los hashes del segundo nivel contienen claves de meses y valores de matrices de entradas.

En este caso, una salida típica de su código sería algo como (basado en el ejemplo):

{ 2009 => { 8 => [Entry1, Entry2], 9 => [Entry3] }, 2007 => { 10 => [Entry5] }} 

en cuenta que, sin embargo, el orden de los años y meses no se garantiza que sea, en cualquier orden particular. La solución normalmente es pedir las llaves cuando quiera acceder a ellas. Ahora, un código que generaría una salida de este tipo (basado en el diseño de código, aunque se puede hacer mucho Rubier):

@years = {} 
@entries.each do |entry| 
    timeobj = Time.parse(entry.created_at.to_s) 
    year = timeobj.strftime("%Y").to_i 
    month = timeobj.strftime("%m").to_i 
    @years[year] ||= {} # Create a sub-hash unless it already exists 
    @years[year][month] ||= [] 
    @years[year][month] << entry 
end 
+0

Gracias por la iluminarme . – jussi

+0

De nada. Mire la respuesta de Michael_Sepcot para saber cómo hacerlo de una manera más _rubyish_. –

+0

¡Gracias! Finalmente entiendo esto –

4

estoy usando tablas hash en lugar de matrices, porque creo que, probablemente, tiene más sentido aquí. Sin embargo, es bastante trivial volver a usar matrices si eso es lo que prefieres.

entries = [ 
    [2009, 8, 1], 
    [2009, 8, 2], 
    [2009, 9, 3], 
    [2007, 10, 5] 
] 

years = Hash.new 
entries.each { |e| 
    year = e[0] 
    month = e[1] 
    entry = e[2] 

    # Add to years array 
    years[year] ||= Hash.new 
    years[year][month] ||= Array.new 
    years[year][month] << entry 
} 

puts years.inspect 

La salida es: {2007=>{10=>[5]}, 2009=>{8=>[1, 2], 9=>[3]}}

4
# create a hash of hashes of array 
@years = Hash.new do |h,k| 
    h[k] = Hash.new do |sh, sk| 
    sh[sk] = [] 
    end 
end 

@entries.each do |entry| 
    timeobj = Time.parse(entry.created_at.to_s) 
    year = timeobj.year 
    month = timeobj.month 
    @years[year][month] << entry 
end 
8

, usted puede obtener la estructura matriz anidada en una línea mediante el uso de una combinación de group_by s y map:

@entries.group_by {|entry| entry.created_at.year }.map { |year, entries| [year, entries.group_by {|entry| entry.created_at.month }] } 
+0

¿No es group_by una adición de rieles, no simple viejo ruby? –

+1

No, viene en Ruby 1.8.7. –

+0

Ah, lo estaba intentando en Ruby 1.8.6. –

Cuestiones relacionadas