cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
Subject Re: Grouping and Summaries in XSLT
Date Fri, 17 Mar 2000 23:53:33 GMT

Cool!  I was thinking that not(fqhn/@Value =
previous-sibling::ROW/fqhn/@Value)wouldn't do the trick, for some strange
reason, but it seems to do fine.  You should also be able to do
not(fqhn/@Value = following-sibling::ROW/fqhn/@Value), which would match
the last occurance, instead of the first, which would be slightly faster,
since both XT and Xalan tend to be slower with previous-sibling.

Thanks for the insight.


                    John Prevost                                                         
                    <prevost@maya.        To:            
                    com>                  cc:, (bcc: Scott
                    Sent by:              Subject:     Re: Grouping and Summaries in XSLT
                    03/17/00 06:33                                                       
                    Please respond                                                       
                    to cocoon-dev                                                        

> 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
> 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
> provide summaries for each month's memory consumption.   The
> 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
> 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) =
= $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