[XSL-LIST Mailing List Archive Home] [By Thread] [By Date]

Re: [xsl] xsl:sort by a sum()?


Subject: Re: [xsl] xsl:sort by a sum()?
From: "Joerg Heinicke" <joerg.heinicke@xxxxxx>
Date: Thu, 11 Apr 2002 23:03:49 +0200

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



Current Thread
Keywords