user1840734
user1840734

Reputation: 357

XSLT: 'value-of select' using keys and conditions?

this question follows on from previous thread: XSLT: Sorting based on sum of values from other nodes

I can now get my data summed from the other nodes by using keys. What I can't seem to get is the syntax or method needed to now apply conditions to select the data I'm after while using these keys.

Here is a simplified xml I'm using (modified from last one to highlight issues):

<Horses>
    <Horse>
        <ID>1</ID>
        <Name>hrsA</Name>
        <SireID>101</SireID>
        <Age>3</Age>
        <Pace>
            <Stakes>20</Stakes>
            <Wins>0</Wins>
        </Pace>
    </Horse>
    <Horse>
        <ID>2</ID>
        <Name>hrsB</Name>
        <SireID>101</SireID>
        <Age>6</Age>
        <Pace>
            <Stakes>1600</Stakes>
            <Wins>9</Wins>
        </Pace>
    </Horse>
    <Horse>
        <ID>3</ID>
        <Name>hrsC</Name>
        <SireID>101</SireID>
        <Age>3</Age>
        <Trot>
            <Stakes>200</Stakes>
            <Wins>2</Wins>
        </Trot>
    </Horse>
    <Horse>
        <ID>4</ID>
        <Name>hrsD</Name>
        <SireID>101</SireID>
        <Age>4</Age>
        <Pace>
            <Stakes>50</Stakes>
            <Wins>0</Wins>
        </Pace>
        <Trot>
            <Stakes>100</Stakes>
            <Wins>1</Wins>
        </Trot>
    </Horse>
    <Horse>
        <ID>5</ID>
        <Name>hrsE</Name>
        <SireID>101</SireID>
        <Age>3</Age>
        <Pace>
            <Stakes>100</Stakes>
            <Wins>1</Wins>
        </Pace>
        <Trot>
            <Stakes>300</Stakes>
            <Wins>1</Wins>
        </Trot>
    </Horse>
</Horses>
<Sires>
    <Sire>
        <ID>101</ID>
        <Name>srA</Name>
        <LiveFoalsALL>117</LiveFoalsALL>
    </Sire>
</Sires>

In one scenario I need to get the offspring (Horses) belonging to one Sire that are of a certain age.

With no age specified I use a key that sorts on stakes won like so (thanks to Dimitre):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:key name="kOffspring" match="Horse" use="SireID"/>

 <xsl:template match="/*">
  <xsl:apply-templates select="Sires/Sire">
   <xsl:sort select="sum(key('kOffspring', ID)/*/Stakes)"
             data-type="number" order="descending"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="Sire">
    <!-- get the data I want -->
 </xsl:template>
</xsl:stylesheet>

Now, in the 'get the data I want' I'm trying to only get 3 year old horses (Age=3) For instance, to only get srA's 3 year old winning offspring (answer = 2) I need something like this I'm thinking:

<xsl:value-of select="count(key('kOffspring', ID)[Age=3]/*/Wins)"/>

but that does not work... Or do I need to create a new key for each age I want to use with some conditional syntax(?) like:

<xsl:key name="kOffspring" match="Horse[/Age=3]" use="SireID"/>

As you can see I don't really know what I'm doing or if it is even possible :-)

.

The other scenario in a similar vein is needing to count how many horses were winners - answer=4 (not the number of wins, just whether they won at pace or trot ... a third scenario requires either just at pace or just at trot).

I tried this in the Sire template with:

<xsl:value-of select="count(key('kOffspring', ID)/*/Wins &gt; 0)"/>

but that only ever returns a count of one.

If any experts here could help get me going I would appreciate that. I am finding xslt syntax a little confusing and hard to remember compared to other programming languages - I hope I get the swing of it soon as it is very powerful.

Regards, Bryce Stenberg.

Upvotes: 2

Views: 10375

Answers (1)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243469

This transformation:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:key name="kOffspring" match="Horse" use="SireID"/>

 <xsl:template match="/*">
  <xsl:apply-templates select="Sires/Sire">
   <xsl:sort select="sum(key('kOffspring', ID)/*/Stakes)"
             data-type="number" order="descending"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="Sire">
     Sire <xsl:value-of select="concat(ID,' (', Name, ') Stakes: ')"/>
   <xsl:value-of select="sum(key('kOffspring', ID)/*/Stakes)"/>
     3 year old winning offspring: <xsl:value-of
     select="count(key('kOffspring', ID)[Age = 3 and */Wins > 0])"/>
     Offspring that ever were a winner: <xsl:value-of
      select="count(key('kOffspring', ID)[*/Wins > 0])"/>
 </xsl:template>
</xsl:stylesheet>

When applied on the provided XML (the provided fragment, enclosed in a single top element to make it a well-formed XML document):

<t>
    <Horses>
        <Horse>
            <ID>1</ID>
            <Name>hrsA</Name>
            <SireID>101</SireID>
            <Age>3</Age>
            <Pace>
                <Stakes>20</Stakes>
                <Wins>0</Wins>
            </Pace>
        </Horse>
        <Horse>
            <ID>2</ID>
            <Name>hrsB</Name>
            <SireID>101</SireID>
            <Age>6</Age>
            <Pace>
                <Stakes>1600</Stakes>
                <Wins>9</Wins>
            </Pace>
        </Horse>
        <Horse>
            <ID>3</ID>
            <Name>hrsC</Name>
            <SireID>101</SireID>
            <Age>3</Age>
            <Trot>
                <Stakes>200</Stakes>
                <Wins>2</Wins>
            </Trot>
        </Horse>
        <Horse>
            <ID>4</ID>
            <Name>hrsD</Name>
            <SireID>101</SireID>
            <Age>4</Age>
            <Pace>
                <Stakes>50</Stakes>
                <Wins>0</Wins>
            </Pace>
            <Trot>
                <Stakes>100</Stakes>
                <Wins>1</Wins>
            </Trot>
        </Horse>
        <Horse>
            <ID>5</ID>
            <Name>hrsE</Name>
            <SireID>101</SireID>
            <Age>3</Age>
            <Pace>
                <Stakes>100</Stakes>
                <Wins>1</Wins>
            </Pace>
            <Trot>
                <Stakes>300</Stakes>
                <Wins>1</Wins>
            </Trot>
        </Horse>
    </Horses>
    <Sires>
        <Sire>
            <ID>101</ID>
            <Name>srA</Name>
            <LiveFoalsALL>117</LiveFoalsALL>
        </Sire>
    </Sires>
</t>

produces the wanted, correct result:

 Sire 101 (srA) Stakes: 2370
 3 year old winning offspring: 2
 Offspring that ever were a winner: 4

Alternatively, a probably more efficient solution is to use another key that is composite and its two parts are the SireID and the Age:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:key name="kOffspring" match="Horse" use="SireID"/>
 <xsl:key name="kOffspringBySireIdAndAge" match="Horse"
  use="concat(SireID, '+', Age)"/>

 <xsl:template match="/*">
  <xsl:apply-templates select="Sires/Sire">
   <xsl:sort select="sum(key('kOffspring', ID)/*/Stakes)"
             data-type="number" order="descending"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="Sire">
     Sire <xsl:value-of select="concat(ID,' (', Name, ') Stakes: ')"/>
   <xsl:value-of select="sum(key('kOffspring', ID)/*/Stakes)"/>
     3 year old winning offspring: <xsl:value-of
     select="count(key('kOffspringBySireIdAndAge', concat(ID, '+3'))
                       [*/Wins > 0]
                  )"/>
     Offspring that ever were a winner: <xsl:value-of
      select="count(key('kOffspring', ID)[*/Wins > 0])"/>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the same XML document (above), the same correct result is produced:

 Sire 101 (srA) Stakes: 2370
 3 year old winning offspring: 2
 Offspring that ever were a winner: 4

Upvotes: 3

Related Questions