Re: [xsl] Re: getting all nodes from a certain level in the xml hierarchy

Subject: Re: [xsl] Re: getting all nodes from a certain level in the xml hierarchy
From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx>
Date: Fri, 27 Sep 2002 12:46:52 +0100
Hi Andrew,

> Here Demitre uses the function count(ancestor::*|.) three times
> within the same template - can anyone tell me if this gets
> re-evaluated each time, therefore using a variable would be better,
> or if it gets stored (as some kind of optimisation maybe), so using
> a variable is unecessary?

It will most probably get re-evaluated each time, unless the processor
is very very smart. ancestor::* isn't likely to give you a *huge* node
set (unlike descendant::*, for example), so this probably isn't a
problem, but if it is, you could use a variable as follows:

<xsl:key name="kDepth" match="Folder"
         use="count(ancestor::*)" />

<xsl:template match="/">
  <xsl:for-each select="//Folder">
    <xsl:variable name="depth" select="count(ancestor::*)" />
    <xsl:if test="generate-id() =
                  generate-id(key('kDepth', $depth)[1])">
      <xsl:value-of select="concat('Level ', $depth, ': ')" />
      <xsl:for-each select="key('kDepth', $depth)">
        <xsl:value-of select="concat(@NAME, '; ')" />
      </xsl:for-each>
    </xsl:if>
  </xsl:for-each>
</xsl:template>

There are two potential issues with this method that arise because
you're for-eaching over *all* the Folder elements. One is that it
might prove less efficient than only for-eaching over the *unique*
Folder elements -- for-eaching might be less efficient than using a
predicate to do the filtering. The second is that if you wanted to use
position() anywhere within the outer xsl:for-each, you'd get the
"wrong" value, because the position would be based on the location of
the Folder amongst *all* the Folders rather than amongst the unique
ones.

A compromise, if either of those is a problem, would be to use:

<xsl:key name="kDepth" match="Folder"
         use="count(ancestor::*)" />

<xsl:template match="/">
  <xsl:for-each
    select="//Folder[generate-id() =
                     generate-id(key('kDepth',
                                     count(ancestor::*))[1])">
      <xsl:variable name="depth" select="count(ancestor::*)" />
      <xsl:value-of select="concat('Level ', $depth, ': ')" />
      <xsl:for-each select="key('kDepth', $depth)">
        <xsl:value-of select="concat(@NAME, '; ')" />
      </xsl:for-each>
    </xsl:if>
  </xsl:for-each>
</xsl:template>

where the calculation gets carried out twice per Folder element,
rather than three times or once.

I should point out, though, that I gather that some processors (Saxon,
I think, but there might be others) attempt to save memory by only
actually storing the value of a variable if it's used more than twice,
so using a variable might make no difference whatsoever (aside from
arguably making the code easier to read). As with all performance
questions, the best advice is "try it and see".

Cheers,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/


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


Current Thread