Forum: Ruby on Rails Generating nice tables

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Kevin O. (Guest)
on 2006-01-02 18:37
Making tables in templates is pretty easy, except for one minor problem.
They tend to be fairly ugly.

If you have a model with three attributes, it's very easy to create an
html table that looks like this..

Col1 Col2 Col3
 A    B    D
 A    B    E
 A    C    F
 A    C    G

Which becomes difficult to read when you have a lot of repeated data.
What I would really like to generate should look more like this..

Col1 Col2 Col3
 A    B    D
           E
      C    F
           G

Using rowspans and align=middle as appropriate.  Basically I want to
combine adjacent rows with the same data into a single cell with a
rowspan.

Does anyone know of a simple algorithm to generate such tables (ideally
from a collection of activerecord objects, of course)?

Thanks,
_Kevin
Gerard (Guest)
on 2006-01-02 19:42
(Received via mailing list)
Kevin,

I couldn't give you the ruby syntax being a newby. But wouldn't a
colspan be
easier to use. Then a simple nested loop would suffice.

The algorithm in human language would be something like this:

do loop for all rows
	reset colspan counter
	do loop for all cells
		If  contents next cell = 0
			colspan =  colspan + 1
		else
			set colspan variable and print contents
		end if
	end columnloop
end rowloop

Like I said, I'm a newbie, so sorry for not being able to hand you the
proper
syntax. But flow wise this should work.

Regards,

Gerard.

On Monday 02 January 2006 17:37, Kevin O. tried to type something
like:
>  A    C    G
> Using rowspans and align=middle as appropriate.  Basically I want to
> combine adjacent rows with the same data into a single cell with a
> rowspan.
>
> Does anyone know of a simple algorithm to generate such tables (ideally
> from a collection of activerecord objects, of course)?
>
> Thanks,
> _Kevin

--
"Who cares if it doesn't do anything?  It was made with our new
Triple-Iso-Bifurcated-Krypton-Gate-MOS process ..."

My $Grtz =~ Gerard;
~
:wq!
Kevin O. (Guest)
on 2006-01-02 22:11
Gerard wrote:
> Kevin,
>
> I couldn't give you the ruby syntax being a newby. But wouldn't a
> colspan be
> easier to use. Then a simple nested loop would suffice.
>

Colspans would be easier, but they wouldn't solve the problem.  What I'm
looking for is a table like this..

+===+===+===+
|   |   | D |
+   + B +===+
|   |   | E |
+ A +===+===+
|   |   | F |
+   + C +===+
|   |   | G |
+===+===+===+

I don't see how I could do this with colspans.
Gerard (Guest)
on 2006-01-02 23:27
(Received via mailing list)
Kevin,

Sorry, I misread the valign middle in the previous email. I thought you
just
wanted 'white space' like this.
+===+===+===+
| E | B | D |
+===+   +===+
| A |   | E |
+   +===+===+
|   | C | F |
+   +   +===+
|   |   | G |
+===+===+===+

That wouldn't be that hard. Just don't print out the cells that are
empty.

Quite a challenge you created btw .. :-)

It can be done though. One way could be to keep a counter for every
column,
and print out the whole table at the end of it. This could be done in a
loop.
But, i think, with a lot of columns this could result in poor
performance.

I don't know where you pull your data from but filling a two dimensional
array
could be a step in the right direction. Because you need to keep track
of how
many cell you have left on a row depending on the rowspans of previous
cells.

I've created a quick html file with table and rowspans in it, and it
seems
that keeping counters is a (the only?) way to do it. If you look at the
code
below. The following would be a fact for in the loops. (Assuming you can
go
through the formentioned array, to establish what 'cells' will have the
same
content and therefore calculate the rowspan value):

A cell having a rowspan of 4 means you have 1 less cell on this and the
following 3. (And so on)

I'm willing to give this some more though if you'd like. But in the
Netherlands it's 22:00 right now. And I perform the biggest brain
teasers
usually in the morning, right after an espresso and a sigaret .. :-)

Let me know how you get along sofar.

regards,

Gerard.

<table border="2">
<tr>
  <td rowspan="4">A</td>
  <td>B</td>
  <td>C</td>
  <td>D</td>

</tr>
<tr>
  <td rowspan="3">B</td>
  <td>C</td>
  <td>D</td>
</tr>
<tr>
  <td>B</td>
  <td>C</td>
</tr>
<tr>
  <td>B</td>
  <td>C</td>
</tr>

</table>






On Monday 02 January 2006 21:11, Kevin O. tried to type something
like:
> +===+===+===+
>
> +   + C +===+
>
> |   |   | G |
>
> +===+===+===+
>
> I don't see how I could do this with colspans.

--
"Who cares if it doesn't do anything?  It was made with our new
Triple-Iso-Bifurcated-Krypton-Gate-MOS process ..."

My $Grtz =~ Gerard;
~
:wq!
Steve R. (Guest)
on 2006-01-02 23:48
Perhaps I'm misunderstanding the question. This looks like it can be
handled in the database quite nicely.

Did you check out the SQL GROUP BY clause? If your database supports
ROLLUP, it may be what you are looking for. E.g.,

@result_set = MyModel.find_by_sql('SELECT c1, c2, c3, SUM(c1), SUM(c2),
SUM(c3) WHERE ? GROUP BY c1, c2, c3 WITH ROLLUP', my_criterion)

The resultant table will contain sum values for c1, c2, and c3,
respectively, but additional rows are injected on category breaks. At
these category breaks, the fields are set to null. I.e.,

value, value, nil === break for c3
value, nil, nil === break for c2
nil, nil, nil === break for c1

There's probably some ultra-concise Ruby way to iterate this collection,
setting the appropriate column values to blank except on first
occurrence and to 'subtotal' at the break, 'grand total' when all are
nil. I haven't thought about that.

Does this help?

Kevin O. wrote:
> Making tables in templates is pretty easy, except for one minor problem.
> They tend to be fairly ugly.
>
> If you have a model with three attributes, it's very easy to create an
> html table that looks like this..
>
> Col1 Col2 Col3
>  A    B    D
>  A    B    E
>  A    C    F
>  A    C    G
>
> Which becomes difficult to read when you have a lot of repeated data.
> What I would really like to generate should look more like this..
>
> Col1 Col2 Col3
>  A    B    D
>            E
>       C    F
>            G
>
> Using rowspans and align=middle as appropriate.  Basically I want to
> combine adjacent rows with the same data into a single cell with a
> rowspan.
>
> Does anyone know of a simple algorithm to generate such tables (ideally
> from a collection of activerecord objects, of course)?
>
> Thanks,
> _Kevin
Kevin O. (Guest)
on 2006-01-02 23:57
Steve R. wrote:
> Perhaps I'm misunderstanding the question. This looks like it can be
> handled in the database quite nicely.
>
> Did you check out the SQL GROUP BY clause? If your database supports
> ROLLUP, it may be what you are looking for. E.g.,
>

This is an interesting idea, I will have to look into it some more.
Gerard (Guest)
on 2006-01-03 00:03
(Received via mailing list)
Steve,

Very nifty what you wrote down here. Getting ahead of things, IMHO, it
would
make a nice practical performance test to see what mysql does with this
in
comparison to ruby. I must admit there's some curiosity on this side ..
;-)

Regards,

Gerard.

On Monday 02 January 2006 22:48, Steve R. tried to type something
like:
> respectively, but additional rows are injected on category breaks. At
>
> >  A    B    D
> >       C    F
> > _Kevin
--
"Who cares if it doesn't do anything?  It was made with our new
Triple-Iso-Bifurcated-Krypton-Gate-MOS process ..."

My $Grtz =~ Gerard;
~
:wq!
Jamie M. (Guest)
on 2006-01-03 00:21
(Received via mailing list)
On 1/2/06, Kevin O. <removed_email_address@domain.invalid> wrote:
>
> I don't see how I could do this with colspans.
The HTML that generates the output you want looks like this:

<table border="1">
  <tr>
    <td rowspan="4" valign="middle">A</td>
    <td rowspan="2" valign="middle">B</td>
    <td rowspan="1" valign="middle">D</td>
  </tr><tr>
    <td rowspan="1" valign="middle">E</td>
  </tr><tr>
    <td rowspan="2" valign="middle">C</td>
    <td rowspan="1" valign="middle">F</td>
  </tr><tr>
    <td rowspan="1" valign="middle">G</td>
  </tr>
</table>

The following methods, stuffed in a helper, will print out a table
like that when given an array.  If you want to send in an arbitrary
collection of AR objects, either adjust the methods, or use Array#map.

def bracketted_table(arr)
  "<table>\n  <tr>\n#{table_brackets(arr)}</table>\n"
end

def table_brackets(arr, depth=0, start=0)
  result = ''
  return result if arr[start].nil?

  height = 2 ** (Math.log(arr.length).ceil - depth)

  result << "    <td rowspan=\"#{height}\"
valign=\"middle\">#{arr[start]}</td>\n"

  result << table_brackets(arr, depth+1, start*2+1)
  result << table_brackets(arr, depth+1, start*2+2)

  if depth == Math.log(arr.length).ceil
    result << "  </tr>\n"
    result << "  <tr>\n" unless start == arr.length - 1
  end
  return result
end

To test, try this:
    test_array = ['A','B','C','D','E','F','G']
    puts bracketted_table(test_array)

The only quirks from a standard walk-binary-tree-stored-in-an-array
algorithm are due to the logic of adding in the <tr> and </tr> tags
around the various entries.  Also note that if your result set does
not have 2^n - 1 elements in it (3, 7, 15, etc), it will probably
render looking a bit screwed up.

- Jamie M.
Steve R. (Guest)
on 2006-01-03 01:30
I can't give you a good benchmark comparison to MySQL-to-Ruby but it
might be irrelevant. Remember, aggregate functions in SQL will most
often return fewer rows than had you queried the entire database and
then aggregated the data yourself. Moving the data from your database
server to your Web application can be expensive.

Even running the database locally, I find that the aggregation I
described runs a bit quicker than returning a complete result set and
iterating the list (and I didn't even write the code to aggregate the
results).

Sometimes the practicality of using database functions so far outweighs
the benefits of loose coupling that find_by_sql is a good choice.

I'm still not clear if this solves the initial problem, though.


Gerard wrote:
> Steve,
>
> Very nifty what you wrote down here. Getting ahead of things, IMHO, it
> would
> make a nice practical performance test to see what mysql does with this
> in
> comparison to ruby. I must admit there's some curiosity on this side ..
> ;-)
>
> Regards,
>
> Gerard.
>
> On Monday 02 January 2006 22:48, Steve R. tried to type something
> like:
>> respectively, but additional rows are injected on category breaks. At
>>
>> >  A    B    D
>> >       C    F
>> > _Kevin
> --
> "Who cares if it doesn't do anything?  It was made with our new
> Triple-Iso-Bifurcated-Krypton-Gate-MOS process ..."
>
> My $Grtz =~ Gerard;
> ~
> :wq!
Gerard (Guest)
on 2006-01-03 13:07
(Received via mailing list)
Steve,

On Tuesday 03 January 2006 00:30, Steve R. tried to type something
like:
> I can't give you a good benchmark comparison to MySQL-to-Ruby but it
> might be irrelevant. Remember, aggregate functions in SQL will most
> often return fewer rows than had you queried the entire database and
> then aggregated the data yourself. Moving the data from your database
> server to your Web application can be expensive.
So true! Your absolutely right (I gues it was late yesterday). Seen some
bad
examples of this in the past. pulling 100's of MB's from a db server
into a
local office network to query only of a couple of rows.

> Even running the database locally, I find that the aggregation I
> described runs a bit quicker than returning a complete result set and
> iterating the list (and I didn't even write the code to aggregate the
> results).
> Sometimes the practicality of using database functions so far outweighs
> the benefits of loose coupling that find_by_sql is a good choice.
Again I agree completely .. :-)

> I'm still not clear if this solves the initial problem, though.
Curious myself.

Regards,

Gerard.

> >
> >> >       C    F
> >> > _Kevin
> >
> > --
> > "Who cares if it doesn't do anything?  It was made with our new
> > Triple-Iso-Bifurcated-Krypton-Gate-MOS process ..."
> >
> > My $Grtz =~ Gerard;
> > ~
> >
> > :wq!

--
"Who cares if it doesn't do anything?  It was made with our new
Triple-Iso-Bifurcated-Krypton-Gate-MOS process ..."

My $Grtz =~ Gerard;
~
:wq!
This topic is locked and can not be replied to.