XPath Q: position of given node in arbitrary nodelist

A

Angus McIntyre

Can this be done with XPath?

I have input XML that is basically of the form:

<list>
<item att="a">s1</item>
<item att="a">s2</item>
<item att="b">s3</item>
<item att="c">s4</item>
<item att="c">s5</item>
</list>

I need to produce output of the form:

1. s1, s2
2. s3
3. s4, s5

Another way of looking at this is to say that I need to know the
position of a given '<item>' node in the list of item nodes whose
attribute 'att' is not the same as that of the preceding node.

(It's a little more complex because I actually need to test two
attributes for non-equality with the attributes of the preceding
sibling, but that's OK).

Is there a way to either (a) get a count of preceding siblings of the
current context node that pass a given test (the test depending on
relative properties of the siblings), or (b) to get the position() of
the current context node in a list of nodes which is _not_ the same as
the current context nodelist.

Advice, even of the form "No, it's not possible, give up", would be very
welcome.

Thanks

Angus
 
D

Dimitre Novatchev

This is a grouping problem and a good solution is to use the Muenchian
method for grouping like this:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:eek:utput method="text"/>

<xsl:key name="kItemAtt" match="item" use="@att"/>

<xsl:template match="/">
<xsl:for-each
select="/*/item[generate-id()
=
generate-id(key('kItemAtt',
@att)[1]
)
]">
<xsl:value-of select="concat(position(), '. ')"/>

<xsl:for-each select="key('kItemAtt', @att)">
<xsl:value-of select="."/>
<xsl:if test="not(position() = last())">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:for-each>

</xsl:template>
</xsl:stylesheet>

When this transformation is applied on your source.xml:

<list>
<item att="a">s1</item>
<item att="a">s2</item>
<item att="b">s3</item>
<item att="c">s4</item>
<item att="c">s5</item>
</list>

the wanted result is produced:

1. s1, s2
2. s3
3. s4, s5


=====
Cheers,

Dimitre Novatchev.
http://fxsl.sourceforge.net/ -- the home of FXSL


Angus McIntyre said:
Can this be done with XPath?

I have input XML that is basically of the form:

<list>
<item att="a">s1</item>
<item att="a">s2</item>
<item att="b">s3</item>
<item att="c">s4</item>
<item att="c">s5</item>
</list>

I need to produce output of the form:

1. s1, s2
2. s3
3. s4, s5

Another way of looking at this is to say that I need to know the
position of a given '<item>' node in the list of item nodes whose
attribute 'att' is not the same as that of the preceding node.

(It's a little more complex because I actually need to test two
attributes for non-equality with the attributes of the preceding
sibling, but that's OK).

Is there a way to either (a) get a count of preceding siblings of the
current context node that pass a given test (the test depending on
relative properties of the siblings), or (b) to get the position() of
the current context node in a list of nodes which is _not_ the same as
the current context nodelist.

Advice, even of the form "No, it's not possible, give up", would be very
welcome.

Thanks

Angus
Kadrey]
 
A

Angus McIntyre

Dimitre Novatchev said:
This is a grouping problem and a good solution is to use the Muenchian
method for grouping like this:

Thank you very much for your solution.

I actually found one of my own, which is less elegant, but which I shall
post here anyway just in case it's of use to someone. (Mine generates a
few stray commas, which would be easy enough to fix, but would obfuscate
the code a little).

<xsl:template match="list">
<xsl:apply-templates select="item[position() = 1 or
@att != preceding-sibling::item[1]/@att]"/>
</xsl:template>

<xsl:template match="item">
<xsl:variable name="att"><xsl:value-of select="@att"/></xsl:variable>
<p><xsl:value-of select="position()"/>.
<xsl:value-of select="text()"/>,
<xsl:for-each select="following-sibling::item[@att = $att]">
<xsl:value-of select="text()"/>,
</xsl:for-each>
</p>
</xsl:template>

Thanks again for your help,

Angus
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,481
Members
44,900
Latest member
Nell636132

Latest Threads

Top