[xsl] Re: Grouping and sorting using an unknown node name

Subject: [xsl] Re: Grouping and sorting using an unknown node name
From: Dimitre Novatchev <dnovatchev@xxxxxxxxx>
Date: Mon, 7 Oct 2002 22:12:40 -0700 (PDT)
--- Trem Stamp wrote:
> Hi,
> 
> I have been looking on various sites for ideas on the best way to do
> some
> grouping/sorting, and I'm unsure of the best way to tackle it.
> I've looked at the xsl:key but am not sure that this is appropriate
> for
> what
> I'm doing.  The problem is that I have an unknown number of
> element readings with unknown names (see example below), which means
> I
> don't
> see how I can define an xsl:key.  For example, the readings
> below contain Cond,TP and Turb.  The xml to be styled will vary (ie.
> there
> may be less than these or more with names that cannot be known
> beforehand),
> although all elements will have a child node called <ordinate>.
> 
> What I need to be able to do is group by the reading type (in
> alphabetical
> order), and for each reading type values, they need to be in date
> order.
> 
> So for example the result would be:
> 
> Cond
> 1993-01-11         165
> 1993-02-10         154
> 
> TP
> 1993-01-11         0
> 1993-02-10         465
> 
> Turb
> 1993-01-11         0
> 1993-02-10         23
> 
> I'm thinking along the lines of finding out how many reading types
> there are
> per reading (ie. in this case 3), and then for each reading
> call a template which selects the first node of each reading which
> has
> an
> ordinate value and repeat till n. I don't know whether this is the
> way
> to go
> about this and still not sure of how to group by date in this
> instance.
> 
> Any advice would be much appreciated.
> 
> I'm using a Saxon windows binary processor
> 
> I have the following sample xml:
> 
> <Collection>
>  <Group>
>   <Reading>
>    <Date>1993-02-10</Date>
>    <SiteNumber>991001</SiteNumber>
>    <Cond>
>     <ordinate>154</ordinate>
>    </Cond>
>    <TP>
>     <ordinate>465</ordinate>
>    </TP>
>    <Turb>
>     <ordinate>23</ordinate>
>    </Turb>
>   </Reading>
>  </Group>
>  <Group>
>   <Reading>
>    <Date>1993-01-11</Date>
>    <SiteNumber>991001</SiteNumber>
>    <Cond>
>     <ordinate>165</ordinate>
>    </Cond>
>    <TP>
>     <ordinate>0</ordinate>
>    </TP>
>    <Turb>
>     <ordinate>0</ordinate>
>    </Turb>
>   </Reading>
>  </Group>
>  ................
> </Collection>
> 
> Thanks,
> 
> Trem

Hi Trem,

Here's a solution to this problem:

With your source xml:
--------------------
<Collection>
 <Group>
  <Reading>
   <Date>1993-02-10</Date>
   <SiteNumber>991001</SiteNumber>
   <Cond>
    <ordinate>154</ordinate>
   </Cond>
   <TP>
    <ordinate>465</ordinate>
   </TP>
   <Turb>
    <ordinate>23</ordinate>
   </Turb>
  </Reading>
 </Group>
 <Group>
  <Reading>
   <Date>1993-01-11</Date>
   <SiteNumber>991001</SiteNumber>
   <Cond>
    <ordinate>165</ordinate>
   </Cond>
   <TP>
    <ordinate>0</ordinate>
   </TP>
   <Turb>
    <ordinate>0</ordinate>
   </Turb>
  </Reading>
 </Group>
</Collection>


this tramsformation:
-------------------
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
 xmlns:vendor="urn:schemas-microsoft-com:xslt">
  
  <xsl:output method="text"/>
  
  <xsl:key name="krByName" 
           match="Reading/*[not(self::Date or self::SiteNumber)]"
           use="name()"/>
           
  <xsl:key name="krbyDate" 
           match="Date"
           use="."/>
           
  <xsl:template match="/">
   <xsl:for-each select="/*/*/Reading/*[
                                      generate-id() 
                                     = 
                                      generate-id(key('krByName',
                                                      name()
                                                      )
                                                      [1]
                                                 )
                                      ]">
     <xsl:text>&#xA;</xsl:text>
     <xsl:value-of select="name()"/>
     
     <xsl:for-each 
       select="/*/*/Reading/Date[
                               generate-id() 
                              = 
                               generate-id(key('krbyDate',
                                                .
                                               )
                                                [1]
                                           )
                               ]
                                  /following-sibling::*
                                    [name() = name(current())]">
          <xsl:sort select="preceding-sibling::Date"/>
     
     <xsl:value-of select="concat('&#xA;', 
                                  preceding-sibling::Date, 
                                  '&#9;', 
                                  .)"/>
     
     </xsl:for-each>

     <xsl:text>&#xA;</xsl:text>

   </xsl:for-each>

  </xsl:template>
</xsl:stylesheet>

when applied produces exactly the wanted result:


Cond
1993-01-11	 165 
1993-02-10	 154 

TP
1993-01-11	 0 
1993-02-10	 465 

Turb
1993-01-11	 0 
1993-02-10	 23 


Hope this helped.



=====
Cheers,

Dimitre Novatchev.
http://fxsl.sourceforge.net/ -- the home of FXSL

__________________________________________________
Do you Yahoo!?
Faith Hill - Exclusive Performances, Videos & More
http://faith.yahoo.com

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread