Re: [xsl] job for xsl:key? (XSL 1.0 question)

Subject: Re: [xsl] job for xsl:key? (XSL 1.0 question)
From: "Mukul Gandhi" <gandhi.mukul@xxxxxxxxx>
Date: Wed, 21 Feb 2007 23:06:11 +0530
For your modified problem description, you would need the following
XSLT stylesheet:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
                      xmlns:common="http://exslt.org/common";
                      exclude-result-prefixes="common"
                      version="1.0">

<xsl:output method="xml" indent="yes"/>

<xsl:key name="by-type" match="Record[class = 'placed']" use="type" />

<xsl:template match="/x">
  <totals>
    <xsl:variable name="rtf">
      <xsl:for-each select="Records/Record[class =
'placed'][generate-id() = generate-id(key('by-type', type)[1])]">
       <temp><xsl:value-of select="count(key('by-type', type)) *
../../vrType/option[@value = current()/type]/@benchmark1" /></temp>
      </xsl:for-each>
    </xsl:variable>
    <benchmark val="{sum(common:node-set($rtf)/temp)}" />
  </totals>
</xsl:template>

</xsl:stylesheet>

The XML file is:

<?xml version="1.0"?>
<x>
 <Records>
   <Record>
     <type>1</type>
     <class>unplaced</class>
     <count>3</count>
   </Record>
   <Record>
     <type>2</type>
     <class>placed</class>
     <count>1</count>
   </Record>
   <Record>
     <type>2</type>
     <class>unplaced</class>
     <count>8</count>
   </Record>
   <Record>
     <type>3</type>
     <class>met45</class>
     <count>1</count>
   </Record>
   <Record>
     <type>3</type>
     <class>placed</class>
     <count>5</count>
   </Record>
   <Record>
     <type>3</type>
     <class>unplaced</class>
     <count>14</count>
   </Record>
 </Records>
 <vrType>
   <option value="1" benchmark1="540.00" benchmark2="540.00"
benchmark3="720.00" />
   <option value="2" benchmark1="1101.00" benchmark2="1101.00"
benchmark3="1468.00" />
   <option value="3" benchmark1="1801.50" benchmark2="1801.50"
benchmark3="2402.00" />
 </vrType>
</x>

Please note - Now, the key definition has changed.

You mentioned - If I change the '1' in generate-id(key('by-type',
@type)[1]) to '2'
Answer to this is - here 1 cannot be changed to 2. Having [1] is part
of standard Muenchian grouping algorithm.

I hope you can adapt this stylesheet to suit your needs.

On 2/21/07, Steve <subsume@xxxxxxxxx> wrote:
Applying this I'm stumped. In my version I am parsing nodes with
class='placed'. You will notice in the $EMP XML there are 1 type 2s
and 5 type 3s. But the below XML only returns 1101 (only counts the 1
type=2).

If I change the '1' in generate-id(key('by-type', @type)[1]) to '2',
it counts the 5 type=3's

XML contents of $EMP

<Records>
  <Record>
     <type>1</type>
     <class>unplaced</class>
     <count>3</count>
  </Record>
  <Record>
     <type>2</type>
     <class>placed</class>
     <count>1</count>
  </Record>
  <Record>
     <type>2</type>
     <class>unplaced</class>
     <count>8</count>
  </Record>
  <Record>
     <type>3</type>
     <class>met45</class>
     <count>1</count>
  </Record>
  <Record>
     <type>3</type>
     <class>placed</class>
     <count>5</count>
  </Record>
  <Record>
     <type>3</type>
     <class>unplaced</class>
     <count>14</count>
  </Record>
</Records>

XML contents of $types
                       <vrType>
                               <option value="1" benchmark1="540.00"   benchmark2="540.00"
       benchmark3="720.00"     />
                               <option value="2" benchmark1="1101.00"  benchmark2="1101.00"
       benchmark3="1468.00"    />
                               <option value="3" benchmark1="1801.50"  benchmark2="1801.50"
       benchmark3="2402.00"    />
                       </vrType>

XSL-----
<td>
       <xsl:variable name="rtfm">
               <xsl:for-each select="$EMP[class='placed' and generate-id() =
generate-id(key('byType',type)[1])]">
                       <temp>
                            <xsl:value-of
select="sum(key('byType',current()/type)[class='placed']/count) *
$types/option[@value=current()/type]/@benchmark1" /></temp>
               </xsl:for-each>
       </xsl:variable>
       $<xsl:value-of select="sum(msxsl:node-set($rtfm)/temp)" />
</td>



On 2/20/07, Mukul Gandhi <gandhi.mukul@xxxxxxxxx> wrote:
> Here is another possible solution (using node-set extension function):
>
> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
>                        xmlns:common="http://exslt.org/common";
>                        exclude-result-prefixes="common"
>                        version="1.0">
>
>  <xsl:output method="xml" indent="yes"/>
>
>  <xsl:key name="by-type" match="contact" use="@type" />
>
>  <xsl:template match="/x">
>    <totals>
>      <xsl:variable name="rtf">
>        <xsl:for-each select="contacts/contact[generate-id() =
> generate-id(key('by-type', @type)[1])]">
>         <temp><xsl:value-of select="count(key('by-type', @type)) *
> ../../types/type[@value = current()/@type]/@benchmark1" /></temp>
>        </xsl:for-each>
>      </xsl:variable>
>      <benchmark val="{sum(common:node-set($rtf)/temp)}" />
>    </totals>
>  </xsl:template>
>
> </xsl:stylesheet>
>
> When the above stylesheet is applied to the following XML:
>
> <?xml version="1.0"?>
> <x>
>   <types>
>      <type value="1" benchmark1="540" />
>      <type value="2" benchmark1="640" />
>      <type value="3" benchmark1="740" />
>   </types>
>   <contacts>
>      <contact type="1" />
>      <contact type="2" />
>      <contact type="3" />
>      <contact type="3" />
>   </contacts>
> </x>
>
> The output produced is:
>
> <?xml version="1.0" encoding="UTF-8"?>
> <totals>
>   <benchmark val="2660"/>
> </totals>
>
> On 2/20/07, Steve <stephen@xxxxxxxxx> wrote:
> > I could do the following easily by making a recursive template and
> > then looping through the contacts, and passing on the corresponding
> > benchmark value.  But could xsl:key make for a shorter, more elegant
> > solution?
> >
> > Instead of performing an addition for each <contact>, could I simply
> > sum() them at once, somehow?
> >
> > With the following XML
> >
> > <types>
> >    <type value="1" benchmark1="540" />
> >    <type value="2" benchmark1="640" />
> >    <type value="3" benchmark1="740" />
> > </types>
> > <contacts>
> >    <contact type="1" />
> >    <contact type="2" />
> >    <contact type="3" />
> >    <contact type="3" />
> > </contacts>
> >
> > desired output:
> >
> > <totals>
> >    <benchmark val='2660'  />
> > </totals>


--
Regards,
Mukul Gandhi

Current Thread