Reputation: 13
Hello I am new to xml and would like to compare some values using an xsl stylesheet
`<a>
<b> <name>foo</name> </b>
<b> <name>bar</name> </b>
<b> <name>fred</name> </b>
<b> <name>fred</name> </b>
</a>`
I would like to write a style sheet that checks all the b nodes and returns the values that have the same value so using the simple example above i would like the output to resemble :
"Your duplicate strings are fred"
I have used a for each loop to return all the values but comparing the names and returning the duplicates has eluded me.If possible i would like to achieve the comparison by the use of a while type loop.
Thank you for any help.
Upvotes: 1
Views: 162
Reputation: 243469
XSLT 1.0: A simple, solution using keys:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kNameByVal" match="name" use="."/>
<xsl:template match="/*">
Your duplicate strings are: <xsl:text/>
<xsl:apply-templates select=
"b/name[generate-id() = generate-id(key('kNameByVal', .)[2])]"/>
</xsl:template>
<xsl:template match="name">
<xsl:if test="position() >1">, </xsl:if>
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
II. XSLT 2.0 solution:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="vSeq" select="data(/a/b/name)"/>
<xsl:template match="/">
Your duplicate strings are: <xsl:text/>
<xsl:sequence select="$vSeq[index-of($vSeq,.)[2]]"/>
</xsl:template>
</xsl:stylesheet>
III. XPath 2.0 one-liner
$vSeq[index-of($vSeq,.)[2]]
This producws all values in a given sequence, that have duplicates (one from a group of duplicates).
Upvotes: 2
Reputation: 338148
An <xsl:key>
-based solution:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kName" match="b/name" use="text()" />
<xsl:template match="/">
<xsl:for-each select="//b/name">
<xsl:if test="count(key('kName', text())) > 1">
<xsl:value-of select="concat('Your duplicate is: ', text(), '
')" />
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
For large input documents this will be more efficient than a solution that uses a preceding::
check.
Upvotes: 1
Reputation: 501
Using a while loop is against XSLT philosophy, even though it can be done.
There are some much easier way to do what you want, for example:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method='text' />
<xsl:template match="b">
<xsl:if test='preceding::b/name/text()=./name/text()'>
Your duplicate is: <xsl:copy-of select='./name/text()' />
</xsl:if>
</xsl:template>
</xsl:stylesheet>
This is looking for node b, and checking if a preceding b node has the same name text
Upvotes: 1