2009-08-05 4 views
5

J'ai une question rubis débutant sur les tableaux multidimensionnels.Comment faire un tableau multidimensionnel dynamique en ruby?

Je souhaite trier les entrées par année et par mois. Donc, je veux créer un tableau multidimensionnel qui contiendrait des années -> mois -> entrées du mois

Donc, le tableau serait comme:

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

Maintenant, j'ai:

@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 

mais lorsque je tente de parcourir le tableau des années, je reçois: « méthode non définie par » pour 2009: Fixnum »

ont aussi essayé:

@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 

Répondre

11

Vous obtenez l'erreur car un FixNum (c'est-à-dire un nombre) est poussé sur le tableau, dans la ligne @years.push(year).

Votre approche d'utiliser Arrays pour commencer est un peu erronée; un tableau est parfait pour contenir une liste ordonnée d'éléments. Dans votre cas, vous avez un mappage des clés aux valeurs, ce qui est parfait pour un hachage.

Dans le premier niveau, les clés sont des années, les valeurs sont des hachages. Les hachages du deuxième niveau contiennent des clés de mois et des valeurs de tableaux d'entrées.

Dans ce cas, une sortie typique de votre code ressemblerait à quelque chose comme (en fonction de votre exemple):

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

Notez que, cependant, l'ordre des années et des mois n'est pas garanti dans tous les ordre particulier. La solution consiste normalement à commander les clés chaque fois que vous voulez y accéder. Maintenant, un code qui générerait une telle sortie (en fonction de votre mise en page de code, bien que peut être beaucoup 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

Merci pour me éclairer . – jussi

+0

De rien. Regardez la réponse de Michael_Sepcot pour savoir comment faire d'une manière plus _rubyish_. –

+0

Merci! Enfin je comprends cela –

4

J'utilise des tables de hachage au lieu de tableaux, parce que je pense que probablement a plus de sens ici. Cependant, il est assez trivial de revenir à l'utilisation de tableaux si c'est ce que vous préférez.

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 sortie est: {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

Vous pouvez obtenir la structure de tableau imbriqué dans une ligne en utilisant une combinaison de group_by s et map:

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

Est-ce que group_by n'est pas une addition de rails, pas de vieux rubis? –

+1

Non, il s'agit de Ruby 1.8.7. –

+0

Ah - Je l'essayais sur Ruby 1.8.6. –