This is the mail archive of the
xsl-list@mulberrytech.com
mailing list .
Re: xsl:sort by a sum()?
- From: "Joerg Heinicke" <joerg dot heinicke at gmx dot de>
- To: <xsl-list at lists dot mulberrytech dot com>
- Date: Thu, 11 Apr 2002 23:03:49 +0200
- Subject: Re: [xsl] xsl:sort by a sum()?
- References: <00a201c1e193$f2e1a200$05963bc8@ar.yahoo.com>
- Reply-to: xsl-list at lists dot mulberrytech dot com
It's not so difficult. With using current(), which returns the current node,
you can change your sum()-function, so that it works in the <xsl:sort/>.
<xsl:template match="/">
<xsl:for-each select="//Fruit[@name and
not(@name=preceding::Fruit/@name)]">
<xsl:sort select="sum(//Fruit[@name=current()/@name]/@amount)"/>
<xsl:value-of select="@name"/>
<xsl:text> : </xsl:text>
<xsl:value-of select="sum(//Fruit[@name=current()/@name]/@amount)"/>
<br/>
</xsl:for-each>
</xsl:template>
But this makes the code not better. For this 9 lines of code all your XML
nodes have to be accessed at least 3 times, because of '//'. Furthermore you
are using the preceding-axis. So for every <Fruit> all nodes before this
node are accessed again. So a little bit bigger file will increase your
processing times extremely.
The first step for optimization is to replace the '//' by a more explicit
XPATH: '/root/FruitBasket/Fruit' (I added <root> as document element.) So
the processor knows where to find the <Fruit>s.
Then you can change the template structure instead of using <xsl:for-each>:
<xsl:template match="root">
<xsl:apply-templates select="FruitBasket/Fruit[@name and
not(@name=preceding::Fruit/@name)]">
<xsl:sort
select="sum(/root/FruitBasket/Fruit[@name=current()/@name]/@amount)"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="Fruit">
<xsl:value-of select="@name"/>
<xsl:text> : </xsl:text>
<xsl:value-of
select="sum(/root/FruitBasket/Fruit[@name=current()/@name]/@amount)"/>
<br/>
</xsl:template>
Then I replace the preceding-axis by the preceding-sibling-axis, so that
only the preceding nodes on the same level are accessed.
select="FruitBasket/Fruit[@name and
not(@name=../preceding-sibling::FruitBasket/Fruit/@name)]"
And now forget this and I show you a complete different method. It's called
Muenchian Method and explained at
http://www.jenitennison.com/xslt/grouping/muenchian.xml.
<!-- building a key-table where the <Fruit>s are 'grouped' by its @name -->
<xsl:key name="Fruits" match="Fruit" use="@name"/>
<xsl:template match="root">
<!-- apply template on the first node of every 'group' -->
<xsl:apply-templates select="FruitBasket/Fruit[count( . | key('Fruits',
@name)[1] ) = 1]">
<!-- key('Fruits', @name) returns all <Fruit>s with this @name -->
<xsl:sort select="sum(key('Fruits', @name)/@amount)"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="Fruit">
<xsl:value-of select="@name"/>
<xsl:text> : </xsl:text>
<xsl:value-of select="sum(key('Fruits', @name)/@amount)"/>
<br/>
</xsl:template>
This is really easy code, more readable and really time-saving.
hope this helps,
Joerg
> <FruitBasket location="kitchen">
> <Fruit name="Apples" amount="10"/>
> <Fruit name="Oranges" amount="20"/>
> </FruitBasket>
> <FruitBasket location="bedroom">
> <Fruit name="Apples" amount="8"/>
> <Fruit name="Oranges" amount="7"/>
> </FruitBasket>
>
> And the following <xsl:for-each>:
>
> <xsl:for-each select="//Fruit[@name and
not(@name=preceding::Fruit/@name)]">
> <xsl:variable name="currentFruit" select="@name"/>
> <xsl:value-of select="$currentFruit"><xsl:text> : </xsl:text><xsl:value-of
> select="sum(//Fruit[@name=$currentFruit]/@amount)"/><br/>
> </xsl:for-each>
>
> After processing with Saxon 6.5.1 I get this:
> Apples: 18
> Oranges: 27
>
> Which is fine and dandy, but I've got 25 FruitBaskets with different
amount
> of fruits each, so I need to sort them by the sum of each Fruit among
> FruitBaskets. Is there an Xpath expression that could achieve that? I had
> tried many, and also searched in the list, but I couldn't find something
> similar to my case.
>
> Many thanks in advance for the tip.
>
> Cheers,
>
> Juan
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list