Subject: Re: [xsl] comparing strings From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx> Date: Tue, 2 Jan 2001 11:13:13 +0000 |
Hi Oliver, > Is there someone out there who can answer my simple question: > How to compare strings with XPath/XSLT, something like strcmp() in C > or compareTo() in Java? > I want a pure XSLT solution without using extensions. The short answer is that there's no XPath function or operator for comparing strings. Depending on the context of the problem, you might be able to use xsl:sort to order strings alphabetically. If you can't, then you have to use a named template to do the comparison for you. Looking at your particular example, I think that the easiest way to approach this is to apply templates to the term that either is the input term or the term immediately following the input term in alphabetical order, with a template that operates on the term by outputting it and the next 9 terms, i.e.: <xsl:template match="term"> <xsl:for-each select=". | following-sibling::term[position() < 10]"> <!-- display in some appropriate way --> </xsl:for-each> </xsl:template> So the problem (which is what you're getting at anyway) is in identifying the term element that has a value that is the same as or follows alphabetically the input term. If you were allowing XSLT 1.1, then you could use xsl:sort to list the terms, including the input term, and select the one you're after: <xsl:template match="longman"> <!-- create a list of terms that includes the input term --> <xsl:variable name="terms"> <input><xsl:value-of select="$input" /></input> <xsl:copy-of select="term" /> </xsl:variable> <xsl:for-each select="$terms"> <!-- sort the terms (including the input term) alphabetically --> <xsl:sort select="." /> <!-- select the term that immediately following the input term within the list --> <xsl:apply-templates select="input/following-sibling::term" /> </xsl:for-each> </xsl:template> As you're not, you have to use a named template to carry out the comparison. Here is a rough-and-ready one that takes two parameters ($first and $second) and returns 'true' if $first comes before $second and 'false' if not (if they're the same, it returns 'true'): <xsl:variable name="charorder" select="'abcdefghijklmnopqrstuvwxyz'" /> <xsl:template name="strcmp"> <xsl:param name="first" /> <xsl:param name="second" /> <xsl:variable name="nFirstFirst" select="string-length( substring-before($charorder, substring($first, 1, 1)))" /> <xsl:variable name="nFirstSecond" select="string-length( substring-before($charorder, substring($second, 1, 1)))" /> <xsl:variable name="restFirst" select="substring($first, 2)" /> <xsl:variable name="restSecond" select="substring($second, 2)" /> <xsl:choose> <xsl:when test="$nFirstFirst < $nFirstSecond">true</xsl:when> <xsl:when test="$nFirstFirst > $nFirstSecond">false</xsl:when> <xsl:when test="not($restFirst)">true</xsl:when> <xsl:when test="not($restSecond)">false</xsl:when> <xsl:otherwise> <xsl:call-template name="strcmp"> <xsl:with-param name="first" select="$restFirst" /> <xsl:with-param name="second" select="$restSecond" /> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:template> The $charorder variable is particularly important as it forms the basis for comparison of the two strings. The above solution lacks support for case-insensitive comparison, and won't deal with hyphens and so on, but it's a start. Supplemented with the following templates: <xsl:template match="longman"> <terms> <!-- apply templates to only the first term in compare mode --> <xsl:apply-templates select="term[1]" mode="compare" /> </terms> </xsl:template> <xsl:template match="term" mode="compare"> <!-- $comparison holds true if the input term is alphabetically before the current term --> <xsl:variable name="comparison"> <xsl:call-template name="strcmp"> <xsl:with-param name="first" select="$inputTerm" /> <xsl:with-param name="second" select="." /> </xsl:call-template> </xsl:variable> <xsl:choose> <xsl:when test="$comparison = 'false'"> <!-- apply templates to the next term in the list in compare mode --> <xsl:apply-templates select="following-sibling::*[1]" mode="compare" /> </xsl:when> <xsl:otherwise> <!-- apply templates to this term, in the default mode, to get output --> <xsl:apply-templates select="." /> </xsl:otherwise> </xsl:choose> </xsl:template> you have your solution. I hope that helps, Jeni --- Jeni Tennison http://www.jenitennison.com/ XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
Re: [xsl] comparing strings (and hu, Miloslav Nic | Thread | RE: [xsl] multiple XML tags -> sing, Kay Michael |
RE: [xsl] Carrying Namespaces throu, Kay Michael | Date | RE: [xsl] strangeness with <xsl:var, Kay Michael |
Month |