11

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>

6 Answers 6

14
# 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
Sign up to request clarification or add additional context in comments.

1 Comment

This answer is so underrated.
10

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>

1 Comment

This example script provide leads with a current version of Ruby and builder to the following output: $ ruby test.rb <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> <to_s/> Replace the puts part with the following and it works: puts "#{xm.target!}"
4

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

1 Comment

That's us posting the same solution at the same time :-)
3

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

1 Comment

I just noticed that, since I ended up changing the script, the output is most definitely not that. Edit in just a sec.
1

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>

Comments

1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.