2010-04-13 25 views
8

¿Cuál es la mejor forma (idealmente una joya, pero un fragmento de código si es necesario) para generar una tabla HTML a partir de una matriz de valores hash?Genera una tabla HTML a partir de una matriz de valores hash en Ruby

Por ejemplo, este conjunto de hashes:

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

debe producir esta tabla:

<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> 

Respuesta

9

Uso del XMLBuilder para esto:

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}" 

salida

<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

esto no parece especialmente difícil de hacer a mano. Dependiendo de dónde se va a utilizar, esto probablemente debería ir en su propio método en alguna parte, pero aquí está el pequeño script que acabo de escribir a:

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 

salida :

<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> 

Obviamente, hay algunas cuestiones - por su parte, se supone que las partidas fila de uno son las mismas que las demás rúbricas. Sin embargo, podría preprocesar y solucionar este problema fácilmente, completando nils en todas las filas para todos los títulos no asignados correctamente.

La razón por la que no hay una joya es que generar una mesa no es realmente una gran empresa. Es increíble lo que puede hacer cuando el cinturón hacia abajo y en realidad código algo por sí mismo :)

+0

Me di cuenta de que, como terminé cambiando el script, la salida definitivamente no es esa. Edita en solo un segundo. – Matchu

+1

Editado. Mucho mejor. – Matchu

4

Puede utilizar 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

Eso es lo que publicamos la misma solución al mismo tiempo :-) –

1

Matchu's answe Me inspiré mucho y lo modifiqué a métodos autodefinidos en lugar de cambiar la clase incorporada (no hagas esto a menos que tengas una buena razón).

Además de generar una tabla, la estructura de Array puede ser mucho más cómoda e intuitiva para acceder a los elementos.

Vamos a toda la tabla almacenada en una matriz 2-D, dicen

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

en el que cada primer elemento sirve como el encabezado de la tabla y el resto es contenido de la tabla. Ahora podemos usar el tabla_matriz bien formada y un atributo de clase tabla para generar un código de tabla 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 

e incrustarlo en.erb archivo

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

la salida sería algo como esto si u ver desde el navegador

<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

Esta respuesta está muy infravalorada. –

+0

lo calificaron :) – zee

+0

¡gracias chicos! :) – Tilo

0

La gema dom que he desarrollado tiene la funcionalidad de lo que quiere hacer. Puedes crear una tabla como esta fácilmente dentro del código de 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>" 
Cuestiones relacionadas