Reputation: 89373
What's the best way (ideally a gem, but a code snippet if necessary) to generate an HTML table from an array of hashes?
For example, this array of hashes:
[{"col1"=>"v1", "col2"=>"v2"}, {"col1"=>"v3", "col2"=>"v4"}]
Should produce this table:
<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>
Upvotes: 11
Views: 12465
Reputation: 168269
The dom gem that I have developed has the functionality of what you want to do. You can create a table like this every easily within Ruby code:
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>"
Upvotes: 1
Reputation: 33752
# 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
Upvotes: 14
Reputation: 1226
Matchu's answer inspired me a lot and I modified it to self-defined methods instead of changing the built-in class (don't do this unless you have a really good reason).
In addition in generating a table, Array's structure maybe much more convenient and intuitive to access elements.
Let the whole table stored in a 2-D array, say
@table_array = [
["Name","Gender","Age"],
["Andy","M","20"],
["Mary","F","19"],
["Tony","M","18"]
]
in which each the first element serves as the table header and the rest is table content. Now we can use the well-formatted table_array and a table class attribute to generate a table html code:
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
and embed it in .erb file
<%= ToTable(@table_array,"table").html_safe %>
the output would be something like this if u see from the browser
<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>
Upvotes: 1
Reputation: 64373
Use the XMLBuilder for this:
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}"
Output
<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>
Upvotes: 10
Reputation: 44110
You can use 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/>"
Upvotes: 4
Reputation: 85862
This doesn't seem particularly difficult to do by hand. Depending on where you're going to use it, this should probably go in its own method somewhere, but here's the little script I just wrote up:
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
Output:
<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>
Obviously, there are some issues - for one, it assumes that row one's headings are the same as all the other headings. You could pre-process and work around this pretty easily, though, by filling in nils in all rows for all headings not properly assigned.
The reason there's not a gem is that generating a table isn't really a huge undertaking. It's amazing what you can do when you buckle down and actually code something yourself :)
Upvotes: 3