2010-04-13 2 views
8

Quelle est la meilleure façon (idéalement une gemme, mais un extrait de code si nécessaire) de générer une table HTML à partir d'un tableau de hachages?Générer un tableau HTML à partir d'un tableau de hachages dans Ruby

Par exemple, ce tableau de hash:

[{"col1"=>"v1", "col2"=>"v2"}, {"col1"=>"v3", "col2"=>"v4"}] 

devrait produire ce tableau:

<table> 
    <tr><th>col1</th><th>col2</th></tr> 
    <tr><td>v1</td><td>v2</td></tr> 
    <tr><td>v3</td><td>v4</td></tr> 
</table> 

Répondre

9

Utilisez le XMLBuilder pour cela:

data = [{"col1"=>"v1", "col2"=>"v2"}, {"col1"=>"v3", "col2"=>"v4"}] 
xm = Builder::XmlMarkup.new(:indent => 2) 
xm.table { 
    xm.tr { data[0].keys.each { |key| xm.th(key)}} 
    data.each { |row| xm.tr { row.values.each { |value| xm.td(value)}}} 
} 
puts "#{xm}" 

Sortie

<table> 
    <tr> 
    <th>col1</th> 
    <th>col2</th> 
    </tr> 
    <tr> 
    <td>v1</td> 
    <td>v2</td> 
    </tr> 
    <tr> 
    <td>v3</td> 
    <td>v4</td> 
    </tr> 
</table> 
4

Cela ne semble pas particulièrement difficile à faire à la main. Selon l'endroit où vous allez l'utiliser, cela devrait probablement aller dans sa propre méthode quelque part, mais voici le petit script que je viens d'écrire jusqu'à:

table.rb:

class Array 
    def to_cells(tag) 
    self.map { |c| "<#{tag}>#{c}</#{tag}>" }.join 
    end 
end 

rows = [{"col1"=>"v1", "col2"=>"v2"}, {"col1"=>"v3", "col2"=>"v4"}] 
headers = "<tr>#{rows[0].keys.to_cells('th')}</tr>" 
cells = rows.map do |row| 
    "<tr>#{row.values.to_cells('td')}</tr>" 
end.join("\n ") 
table = "<table> 
    #{headers} 
    #{cells} 
</table>" 
puts table 

sortie :

<table> 
    <tr><th>col1</th><th>col2</th></tr> 
    <tr><td>v1</td><td>v2</td></tr> 
    <tr><td>v3</td><td>v4</td></tr> 
</table> 

de toute évidence, il y a quelques problèmes - pour un, il suppose que la première rangée de rubriques sont les mêmes que toutes les autres rubriques. Vous pouvez toutefois pré-traiter et contourner ce problème en remplissant nils dans toutes les lignes pour toutes les rubriques qui ne sont pas correctement affectées.

La raison pour laquelle il n'y a pas de gemme est que générer une table n'est pas vraiment une entreprise énorme. Il est incroyable ce que vous pouvez faire lorsque vous boucle vers le bas et le code fait quelque chose vous :)

+0

Je viens de remarquer que, puisque j'ai fini par changer le script, la sortie n'est certainement pas celle-là. Modifier en seulement une seconde. – Matchu

+1

Édité. Beaucoup mieux. – Matchu

4

Vous pouvez utiliser builder:

require 'builder' 

a = [{"col1"=>"v1", "col2"=>"v2"}, {"col1"=>"v3", "col2"=>"v4"}] 
builder = Builder::XmlMarkup.new 
columns = a.first.keys 
builder.table do |t| 
    t.tr do |tr| 
    columns.each do |col| 
     tr.th(col) 
    end 
    end 
    a.each do |row| 
    t.tr do |tr| 
     columns.each do |col| 
     tr.td(row[col]) 
     end 
    end 
    end 
end 
p builder.target 
#=> "<table><tr><th>col1</th><th>col2</th></tr><tr><td>v1</td><td>v2</td></tr><tr><td>v3</td><td>v4</td></tr></table><target/>" 
+0

C'est nous qui publions la même solution en même temps :-) –

1

Réponse de Matchu r m'a beaucoup inspiré et je l'ai modifié pour des méthodes auto-définies au lieu de changer la classe intégrée (ne faites pas cela à moins d'avoir une bonne raison).

En plus de générer une table, la structure de Array peut être beaucoup plus pratique et intuitive pour accéder aux éléments.

Soit toute la table stockée dans une matrice 2-D, dites

@table_array = [ 
       ["Name","Gender","Age"], 
       ["Andy","M","20"], 
       ["Mary","F","19"], 
       ["Tony","M","18"] 
       ] 

dans lequel chaque premier élément sert de l'en-tête de table et le reste est contenu de la table. Maintenant, nous pouvons utiliser le bien formaté table_array et un attribut de classe de table pour générer une table code html:

def ToCell (tag,value) 
    value.map{ |c| "<#{tag}>#{c}</#{tag}>" }.join 
end 

def ToTable (table_array, table_class) 
    headers = "<tr>" + ToCell('th',table_array[0]) + "</tr>" 
    cells = table_array[1..table_array.count].map{ |each_row| 
     "<tr>#{ToCell('td',each_row)}</tr>"    
    }.join 

    table = "<table class=\"#{table_class}\"><thead>#{headers}</thead><tbody>#{cells}</tbody></table>" 
end 

et l'intégrer dans.fichier erb

<%= ToTable(@table_array,"table").html_safe %> 

la sortie serait quelque chose comme ça si u voir à partir du navigateur

<table class="table"> 
    <thead> 
      <tr><th>Name</th><th>Gender</th><th>Age</th></tr> 
    </thead> 
    <tbody> 
      <tr><td>Andy</td><td>M</td><td>20</td></tr> 
      <tr><td>Mary</td><td>F</td><td>19</td></tr> 
      <tr><td>Tony</td><td>M</td><td>18</td></tr> 
    </tbody> 
</table> 
10
# modified from Harish's answer, to take care of sparse hashes: 

require 'builder' 

def hasharray_to_html(hashArray) 
    # collect all hash keys, even if they don't appear in each hash: 
    headers = hashArray.inject([]){|a,x| a |= x.keys ; a} # use array union to find all unique headers/keys        

    html = Builder::XmlMarkup.new(:indent => 2) 
    html.table { 
    html.tr { headers.each{|h| html.th(h)} } 
    hashArray.each do |row| 
     html.tr { row.values.each { |value| html.td(value) }} 
    end 
    } 
    return html 
end 
+2

Cette réponse est tellement sous-estimée. –

+0

l'a fait noter :) – zee

+0

merci, les gars! :) – Tilo

0

Le joyau dom que j'ai développé a la fonctionnalité de ce que vous voulez faire. Vous pouvez créer une table comme celle-ci facilement dans le code Ruby:

require "dom" 
[%w[aaa bbb], %w[ccc ddd]].dom(:td, :tr, :table) 
# => "<table><tr><td>aaa</td><td>bbb</td></tr><tr><td>ccc</td><td>ddd</td></tr></table>" 
Questions connexes