I found this page describing the Muenchian method, but I think I'm applying it wrong.
Consider that this would return a set of ages:
/doc/class/person/descriptive[(@name='age')]/value
1..2..2..2..3..3..4..7
But I would like a nodeset only one node for each age.
1..2..3..4..7
Each of these seem to return all of the values, instead of unique values:
/doc/class/person/descriptive[(@name='age')][not(value=preceding-sibling::value)]/value
/doc/class/person/descriptive[(@name='age')]/value[not(value=preceding-sibling::value)]
What am I missing?
Here's an example:
<root>
<item type='test'>A</item>
<item type='test'>B</item>
<item type='test'>C</item>
<item type='test'>A</item>
<item type='other'>A</item>
<item type='test'>B</item>
<item type='other'>D</item>
<item type=''>A</item>
</root>
And the XPath:
//preceding::item/preceding::item[not(.=preceding-sibling::item)]/text()
Results: A B C D
EDIT: As mousio commented this doesn't capture the last item in a list if it's the only time it appears. Taking that and Fëanor's comment into account, here's a better solution:
/root/item[not(.=preceding-sibling::item)]
Here is the Muenchian version of BQ's answer using his data:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="text"/>
<xsl:key name="item-by-value" match="item" use="."/>
<xsl:template match="/">
<xsl:apply-templates select="/root/item"/>
</xsl:template>
<xsl:template match="item">
<xsl:if test="generate-id() = generate-id(key('item-by-value', normalize-space(.)))">
<xsl:value-of select="."/>
<xsl:text>
</xsl:text>
</xsl:if>
</xsl:template>
<xsl:template match="text()">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
This transform gives
A
B
C
D
key()
lookup above in the template for item
returns a nodeset containing all the item
elements with the same string value as the context node. generate-id()
are guaranteed to generate the same ID for a given node during a single pass through a document. key()
call.For those who still look for a select distinct in XSLT:
With XSLT 2.0, you can use "distinct-values(/doc/class/person/descriptive[(@name='age')]/value)"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With