Link to home
Create AccountLog in
Avatar of unceasing
unceasing

asked on

sort a tokenized string in xsl

I need to sort a string such as "strawberry|blueberry|orange|raspberry|lime|lemon" which appears in an xsl attribute
Avatar of Gertone (Geert Bormans)
Gertone (Geert Bormans)
Flag of Belgium image

XSLT2 is pretty straightforward

        <xsl:for-each select="tokenize(., '\|')">
            <xsl:sort select="." data-type="text" order="ascending"/>
            <xsl:if test="not(position() = 1)">
                <xsl:text>|</xsl:text>
            </xsl:if>
            <xsl:value-of select="."></xsl:value-of>
        </xsl:for-each>
If you need to do this in XSLT1, you will have some issues
- XSLT only sorts on iterating a nodeset, so first you need to make some XML nodeset from the string in a first step and then use some node-set functionality
but node-set() are extension functions bound to particular processors (for processors that support it)... so I need to know which processor you use then
- or build an extension function that does that for you (means step out teh XSLT one way or another and do the sort in JavaScript or C# or whatever)
also this is processor dependent
Avatar of unceasing
unceasing

ASKER

Using the stylesheet such as below,
for a string such as strawberry|orange|grapes
 I get the result of the <xsl:copy-of .. statement for the variable as
<?xml version="1.0" encoding="UTF-16"?><tokens><token>strawberry</token> <token>orange</token> <token>grapes</token> </tokens>

but I'm not able to sort it alphabetically. When trying to implement any kind of sorting I get the error - "The expression does not evaluate to node-set"

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="/">
        <xsl:apply-templates select="//Page"></xsl:apply-templates>
    </xsl:template>
    <xsl:template match="//Page">  
        <xsl:variable name="tokens">
            <tokens>
            <xsl:call-template name="tokenize">
                <xsl:with-param name="string">
                    <xsl:value-of select="//Page/@Keywords"/>
                </xsl:with-param>
                <xsl:with-param name="delimiter">|</xsl:with-param>
            </xsl:call-template>
            </tokens>                
        </xsl:variable>        
      <xsl:copy-of select="$tokens"/>
    </xsl:template>

    <xsl:template name="tokenize">
        <xsl:param name="string"/>
        <xsl:param name="delimiter" select="' '"/>
        <xsl:choose>
            <xsl:when test="$delimiter and contains($string, $delimiter)">
                <token>
                    <xsl:value-of select="substring-before($string, $delimiter)"/>
                </token>
                <xsl:text> </xsl:text>
                <xsl:call-template name="tokenize">
                    <xsl:with-param name="string" select="substring-after($string, $delimiter)"/>
                    <xsl:with-param name="delimiter" select="$delimiter"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <token>
                    <xsl:value-of select="$string"/>
                </token>
                <xsl:text> </xsl:text>
            </xsl:otherwise>
        </xsl:choose>
     </xsl:template>

</xsl:stylesheet>
I'm using MSXML4.0 as the processor.
ASKER CERTIFIED SOLUTION
Avatar of Gertone (Geert Bormans)
Gertone (Geert Bormans)
Flag of Belgium image

Link to home
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
See answer
Works like a charm. Thank you.
Hi Gertone,

I would like to ask a followup question on this.

Now i have all my keywords in the sorted order. Now for each of these  tokens I would like to go back to the page element and get the element's title if the title occurs in the element. Could you please suggest how to write code for this.

<xsl:for-each select="msxsl:node-set($keywords)//keyword">
            <xsl:sort select="." data-type="text" order="ascending"/>
            <xsl:if test="not(position() = 1)">
                <xsl:text>&lt;br&gt;</xsl:text>
            </xsl:if>
            <xsl:variable name="currentindex">
                <xsl:value-of select="."/>
            </xsl:variable>
            <xsl:call-template name="findindex">
                <xsl:with-param name="currentindex">
                    <xsl:value-of select="$currentindex"/>
                </xsl:with-param>
            </xsl:call-template>
        </xsl:for-each>      
    </xsl:template>  
    <xsl:template name="findindex">
        <xsl:param name="currentindex"></xsl:param>
        <div><xsl:value-of select="$currentindex"/></div>
        .......

    </xsl:template>