|
Subject: RE: [xsl] Counting nodes efficiently From: Dimitre Novatchev <dnovatchev@xxxxxxxxx> Date: Thu, 19 Feb 2004 02:19:32 -0800 (PST) |
> > Greetings.
> >
> > I've been using Jeni's method from the XSLT FAQ to assign
> > unique id's to
> > nodes. In order to speed things up, can anyone think of a way
> > that I could
> > store the running totals for the different nodes, rather than
> > having to
> > call the count() function repeatedly? A generalized method
> > would obviously
> > be the best, so that it could be applied to any arbitrary set
> > of nodes,
> > but I don't know if this is even possible.
> >
> > <xsl:template match="*">
> > <xsl:variable name="name" select="name()" />
> > <xsl:element name="{name()}">
> > <xsl:attribute name="id">
> > <xsl:value-of select="concat($name, '-',
> > count(preceding::*[name()= $name]) +
> > count(ancestor::*[name()= $name]))" />
> > </xsl:attribute>
> > <xsl:apply-templates />
> > </xsl:element>
> > </xsl:template>
>
> 3 ways:
>
> 1. Create a node-set by selecting all the elements you wish to count
> and numbering them using position(). You can then query into this
> node-set using the generate-id() function to get the correct number for
> the element you're processing. This only requies one pass of the data
> so its quite efficient.
>
> 2. Write a SAX Filter in java that numbers the elements on their way
> into the transform. You can then select this number as if it was
> already in the data.
>
> 3. If you are using saxon, you can substring the value returned from
> generate-id() after the 'e', as the generated id's take form 'dxxeyy'
> where d is the document number and e is the element number.
As pointed out earlier, just using generate-id() probably solves the
original problem.
I will show here one solution to the problem, because it is interesting,
simple and uses and important xslt design pattern.
In this and similar problems one can re-use the identity transformation,
which processes strictly one node at a time (would a native English
speaker, please, suggest a good name? The best I could arrive at was "one
node at a time identity transformation"):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="node()[1]"/>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
</xsl:stylesheet>
This transformation produces the same results as the more well-known
identity rule.
The difference is that we now have the finest possible grain-level of
controll as every xsl:apply-templates instruction above always selects at
most one node.
It is trivial to add parameters and to override the one-at-a-time identity
for elements. Thus we finally have:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="@* | node()">
<xsl:param name="pnAncestors" select="0"/>
<xsl:param name="pnPreceding" select="0"/>
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="node()[1]">
<xsl:with-param name="pnAncestors"
select="$pnAncestors+1"/>
<xsl:with-param name="pnPreceding"
select="$pnPreceding"/>
</xsl:apply-templates>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]">
<xsl:with-param name="pnAncestors"
select="$pnAncestors"/>
<xsl:with-param name="pnPreceding"
select="$pnPreceding+1"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="*">
<xsl:param name="pnAncestors" select="0"/>
<xsl:param name="pnPreceding" select="0"/>
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:attribute name="_id">
<xsl:value-of select=
"concat(name(), '_',
$pnAncestors, '_',
$pnPreceding
)"/>
</xsl:attribute>
</xsl:copy>
<xsl:apply-templates select="node()[1]">
<xsl:with-param name="pnAncestors"
select="$pnAncestors+1"/>
<xsl:with-param name="pnPreceding"
select="$pnPreceding"/>
</xsl:apply-templates>
<xsl:apply-templates select="following-sibling::node()[1]">
<xsl:with-param name="pnAncestors"
select="$pnAncestors"/>
<xsl:with-param name="pnPreceding"
select="$pnPreceding+1"/>
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
This transformation is not long, it can be written almost mechanically, it
makes exactly one pass over the tree and it has a linear time complexity
(checked with inputs of different size).
Cheers,
Dimitre Novatchev
FXSL developer,
http://fxsl.sourceforge.net/ -- the home of FXSL
Resume: http://fxsl.sf.net/DNovatchev/Resume/Res.html
__________________________________
Do you Yahoo!?
Yahoo! Mail SpamGuard - Read only the mail you want.
http://antispam.yahoo.com/tools
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
| Current Thread |
|---|
|
| <- Previous | Index | Next -> |
|---|---|---|
| RE: [xsl] Counting nodes efficientl, Andrew Welch | Thread | RE: [xsl] Counting nodes efficientl, Wendell Piez |
| [no subject], David Carlisle | Date | RE: [xsl] Counting nodes efficientl, Dimitre Novatchev |
| Month |