RE: [xsl] Continuous path of elements satisfying a condition

Subject: RE: [xsl] Continuous path of elements satisfying a condition
From: "Michael Kay" <mike@xxxxxxxxxxxx>
Date: Sun, 19 Dec 2004 23:13:41 -0000
> 1) I can select the leaves and then all the relevant items with
> 
> <xsl:variable name="leaves" select="//item[@id and not(*) and 
> not (ancestor::item[not(@id)])]"/>
> <xsl:variable name="items" select="$leaves | $leaves/ancestor::item"/>
> 
> Is there a better way?

I don't think there is a more concise way, but there may be a more efficient
way.

You could change the second variable to

select="$leaves/ancestor-or-self::item"

but it's not a big difference.

You might get a performance benefit by going downwards only: this needs a
recursive function (and XSLT 2.0)

<xsl:function name="f:down">
  <xsl:param name="i" as="element(item)"/>
  <xsl:sequence select="if ($i/@id) then 
                          if (child::item) 
                             then for $x in child::item return f:down($x)
                             else $i
                          else ()"/>
</xsl:function>

If @id attributes occur infrequently, this will search a much smaller part
of the tree.

> 
> 2) In the template for an item, I need to loop through the 
> item's children that meet the
> conditions (i.e., that are in $items).  I've tried this
> 
> <xsl:variable name="nitems" select="count($items)"/>
> 
> <xsl:template match="/">
> <xsl:apply-templates select="$items"/>
> </xsl:template>
> 
> <xsl:template match="item">
> <xsl:for-each select="item[count(. | $items) = $nitems]">
> 	...
> </xsl:text>
> </xsl:for-each>
> </xsl:template>
> 
> This is fairly elegant but testing in my environment (MSXML) 
> shows it to take 4 seconds
> while a recursive template that just sums the number of 
> included items that are 
> children of the current item runs about 50 lines but takes 
> half the time to run.
> 

That doesn't surprise me. In XPath 2.0 you can do a set intersection
directly, as $A intersect $B. The XPath 1.0 workaround of $A[count(.|$B) =
count($B)] is likely to be pretty efficient unless the optimizer rewrites it
radically.

Michael Kay
http://www.saxonica.com/

Current Thread