cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From John Prevost <>
Subject Re: Grouping and Summaries in XSLT
Date Fri, 17 Mar 2000 23:33:36 GMT writes:

> The only technique I know of to group without knowing the selection
> criteria of the groups ahead of time, is to recurse into a named template
> for each element passing in a string, add the found selection criteria to
> the string via the concat function, and then test with the contains()
> function if the selection criteria exists withing the string.  It's ugly
> and a bit slow, but it works.  (This would be easy if you had for loops and
> assignable variables in XSLT, but such is the design constraints of the
> language...)

There's a much much much easier way to do this.  It's a little odd to
the imperative-language trained mind, but it grows on you.  (Like a
fungus.)  Key idea: find a set of nodes which contains one item for
each "group" you want.  The item should allow you to get the group
it's associated with.  The easiest choice is usually "the first node
in the group", which translates (when you're working with non
pre-determined groups) to "every node for which no previous node has
the same 'grouping property'".)  I've done this with your example

> In the sample below, the user wanted to group the rows by server, and then
> provide summaries for each month's memory consumption.   The transformation
> is a useful example of how to do two-level grouping using the above
> technique.  It would be nice to paramiterise the templates and simply put
> them in a library, but that would be difficult, I think.  I'm sure the
> named templates could be improved in a couple of ways for better
> performance.  I'll be interested to see what ideas folks come up with.
> BTW, you could also use extensions to do this, but the transformation below
> is totally interoperable.
> The input data:
> <TABLE >
>    <ROW>
>        <fqhn  Value="pebbles"/>
>        <mem_pct_used  Value="54"/>
>        <date Value="20/02/00"/>
>    </ROW>
>    <ROW>
>        <fqhn  Value="pebbles"/>
>        <mem_pct_used  Value="11"/>
>        <date  Value="21/02/00"/>
>    </ROW>
>    <ROW >
>        <fqhn  Value="bambam"/>
>        <mem_pct_used  Value="27"/>
>        <date  Value="10/03/00"/>
>    </ROW>
>    <ROW >
>        <fqhn  Value="pebbles"/>
>        <mem_pct_used  Value="22"/>
>        <date  Value="10/03/00"/>
>    </ROW>
> </TABLE>

 <xsl:template match="TABLE">

   <xsl:for-each select="ROW
                           [not(fqhn/@Value =
    <!-- Find the first occurence of each server name -->


     <xsl:variable name="server" select="fqhn/@Value"/>
     <!-- Remember it because we're lazy -->

     <Server_Configuration_Info Server_id="{$server}"/>

                   [fqhn/@Value = $server]
                   [not(substring(date/@Value, 4, 2) =
                        substring(previous-sibling::ROW[fqhn/@Value = $server]
                                        /date/@Value, 4, 2))]">
      <!-- find the first occurence of each month in the current server -->

      <xsl:variable name="month" select="substring(date/@Value, 4, 2)"/>
      <!-- remember it because we're lazy -->

      <Server_Performance_Info Month="{substring(date/@Value, 4, 2)}"
        Sum="{sum(../ROW[fqhn/@Value = $server]
                        [substring(date/@Value, 4, 2) = $month]
      <!-- output the sum over things with the same date and server -->


> will give you:
> <?xml version="1.0" encoding="UTF-8"?>
>     <Server>
>         <Server_Configuration_Info Server_id="pebbles"/>
>         <Server_Performance_Info Sum="65" Month="02/00"/>
>         <Server_Performance_Info Sum="22" Month="03/00"/>
>     </Server>
>     <Server>
>         <Server_Configuration_Info Server_id="bambam"/>
>         <Server_Performance_Info Sum="27" Month="03/00"/>
>     </Server>

Mine too.  (I tested it with Xalan and Cocoon while writing this mail.)

This can also be used to, in a really heinous way, do things like turn
data organized in "columns" at the top level into data organized in
"rows" (suitable for use in HTML.)  That is:




or vice-versa.

(I'll be happy to share the code with people if they think their
sanity will survive it.  I'll give you a hint: the first step is to
find the longest row.)

In any case--this needs to be fixed some day.  Neither Scott's method
or mine is terribly appealing to the well-balanced soul.


View raw message