Re: [xsl] how to get position of node in node-set

Subject: Re: [xsl] how to get position of node in node-set
From: Wendell Piez <wapiez@xxxxxxxxxxxxxxxx>
Date: Thu, 11 Sep 2003 13:20:08 -0400
Erwin,

At 12:32 PM 9/11/2003, David wrote:
Node sets are sets and so don't have an intrinsic order.
So nodes don't really have a position in a set (you can't for example
sort the nodes and save the sorted list in a set)

Of course you can try, but then in XSLT you have a result-tree-fragment, not a node set, which is where my answer (of a few minutes ago) starts. (Sorry if this assumption on my part was incorrect.)


In XSLT 1.0, whether a variable refers to a node set or to an RTF, depends on how it's created. For example, if (using a key-based technique to de-duplicate) you declare your variable like this:

<xsl:variable name="legend"
   select="//entry[count(.|key('entries-by-text',@text)[1])=1]"/>

you'll have a true node set, in which case all David's admonitions apply. They are not in order within $legend, and if you process them using for-each or apply-templates, they'll be processed in the order they appear in the source document.

If, however, in order to sort the nodes, you say

<xsl:variable name="legend">
  <xsl:apply-templates select="//entry" mode="de-duplicate">
    <xsl:sort select="@text"/>
  </xsl:apply-templates>
</xsl:variable>

you'll get your order but you won't have a node set, rather an RTF.

To convert an RTF to a node-set requires the extension function in XSLT 1.0. Then you can operate on it again. The only things you can do with an RTF are copy it to the result (as many times as you like :-), or turn it into a string and do stuff with that.

(David continues)
You can ask what is the position if the nodes in the set were sorted
into document order, although actually it's not so easy.
If the nodes are siblings and you are on <entry text="ccc"/>
then you want
count(preceding-sibling::entry)+1
however you only want to count nodes that are in $legend
so that would be
count(preceding-sibling::entry[@text=$legend])+1

Unfortunately this counts the preceding siblings whose @text attribute has a string value equal to the value of some node in $legend ... maybe not so useful.


if your text attribute uniquely determines the node,

Right (but Erwin said it wasn't: bummer).


 or something like
count(preceding-sibling::entry[count(.|$legend)=count($legend)]+1
otherwise.

That's very sneaky, and might just work. *If* your nodes are siblings (I'm betting they're not).


They are all pure xpath solutions, or you could use xsl, as in

<xsl:for-each select="$legend">
<xsl:if test="@text='ccc'"><xsl:value-of select="position()"/></xsl:if>

Which suggests a way to avoid the node-set() extension ... assuming $legend is a node set (which has no intrinsic order):


<xsl:template match="entry"/>
  <xsl:variable name="text" select="@text"/>
  <xsl:for-each select="$legend">
    <xsl:sort select="@text"/>
    <xsl:if test="@text=$text">
      <xsl:value-of select="position()"/>
    </xsl:if>
  </xsl:for-each>
</xsl:template>

Who said it's not possible in XSLT 1.0? (Thanks, David.)

Will the language be easier or harder when RTFs disappear and become node sequences? Who's to say?

Cheers,
Wendell


====================================================================== Wendell Piez mailto:wapiez@xxxxxxxxxxxxxxxx Mulberry Technologies, Inc. http://www.mulberrytech.com 17 West Jefferson Street Direct Phone: 301/315-9635 Suite 207 Phone: 301/315-9631 Rockville, MD 20850 Fax: 301/315-8285 ---------------------------------------------------------------------- Mulberry Technologies: A Consultancy Specializing in SGML and XML ======================================================================


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



Current Thread