[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message]

Re: comparing strings

Subject: Re: comparing strings
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Tue, 2 Jan 2001 11:13:13 +0000
xsl compare strings
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() &lt; 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 &lt; $nFirstSecond">true</xsl:when>
      <xsl:when test="$nFirstFirst &gt; $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

PURCHASE STYLUS STUDIO ONLINE TODAY!

Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!

Buy Stylus Studio Now

Download The World's Best XML IDE!

Accelerate XML development with our award-winning XML IDE - Download a free trial today!

Don't miss another message! Subscribe to this list today.
Email
First Name
Last Name
Company
Subscribe in XML format
RSS 2.0
Atom 0.3
Site Map | Privacy Policy | Terms of Use | Trademarks
Free Stylus Studio XML Training:
W3C Member
Stylus Studio® and DataDirect XQuery ™are products from DataDirect Technologies, is a registered trademark of Progress Software Corporation, in the U.S. and other countries. © 2004-2013 All Rights Reserved.