xsl-list
[Top] [All Lists]

Re: [xsl] Re: Dynamic number of sort key component?

2010-05-14 12:00:28
Here is a non-recursive, XSLT 1.0 solution. It assumes that the
maximum possible number of sort keys is statically known, and
specifies the maximum possible number of sort keys:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
 xmlns:my="my:my"

 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <my:sortKeys>
  <key value="a" order="descending"/>
  <key value="c" order="ascending"/>
 </my:sortKeys>

 <xsl:variable name="vSortKeys" select=
 "document('')/*/my:sortKeys/*"/>

 <xsl:template match="/*">
  <xsl:for-each select="row">
   <xsl:sort select="col[(_at_)name=$vSortKeys[1]/@value]"
     order="{$vSortKeys[1]/@order}"/>
   <xsl:sort select="col[(_at_)name=$vSortKeys[2]/@value]"
     order="{$vSortKeys[2]/@order}"/>
   <xsl:sort select="col[(_at_)name=$vSortKeys[3]/@value]"
     order="{$vSortKeys[3]/@order}"/>
   <xsl:sort select="col[(_at_)name=$vSortKeys[4]/@value]"
     order="{$vSortKeys[4]/@order}"/>
   <xsl:sort select="col[(_at_)name=$vSortKeys[5]/@value]"
     order="{$vSortKeys[5]/@order}"/>
   <xsl:sort select="col[(_at_)name=$vSortKeys[6]/@value]"
     order="{$vSortKeys[6]/@order}"/>

    <xsl:copy-of select="."/>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on this XML document:

<table>
 <row>
   <col name="a">1</col>
   <col name="b">2</col>
   <col name="c">3</col>
   <col name="d">4</col>
 </row>
 <row>
   <col name="a">2</col>
   <col name="b">bli</col>
   <col name="c">1</col>
   <col name="d">blu</col>
 </row>
</table>

the wanted, correct result is produced:

    <row>
        <col name="a">2</col>
        <col name="b">bli</col>
        <col name="c">1</col>
        <col name="d">blu</col>
    </row>
    <row>
        <col name="a">1</col>
        <col name="b">2</col>
        <col name="c">3</col>
        <col name="d">4</col>
    </row>


-- 
Cheers,
Dimitre Novatchev
---------------------------------------
Truly great madness cannot be achieved without significant intelligence.
---------------------------------------
To invent, you need a good imagination and a pile of junk
-------------------------------------
Never fight an inanimate object
-------------------------------------
You've achieved success in your field when you don't know whether what
you're doing is work or play




On Fri, May 14, 2010 at 7:22 AM, Michael Kay <mike(_at_)saxonica(_dot_)com> 
wrote:

Try something like this:

<xsl:function name="f:sort" as="element(row)*">
 <xsl:param name="data" as="element(row)"/>
 <xsl:param name="sortColumnNames" as="xs:string*"/>
 <xsl:param name="sortAscDesc" as="xs:string*"/>
 <xsl:variable name="sorted" as="element(row)">
   <xsl:perform-sort select="$data">
     <xsl:sort select="*[name()=$sortColumnNames[last()]"
               order="{$sortAscDesc[last()]}" stable="yes"/>
   </xsl:perform-sort>
 <xsl:variable>
 <xsl:sequence select="if (count($sortColumnNames) eq 1
                       then $sorted
                       else f:sort($sorted, subsequence($sortColumnNames,
2), subsequence($sortAscDesc, 2))"/>
</xsl:function>

Those with grey hair will recognize this as the multi-phase sort process
used by punched card operators, sorting first by the last sort key, then the
last-but-one, and so on.

Regards,

Michael Kay
http://www.saxonica.com/
http://twitter.com/michaelhkay

-----Original Message-----
From: Fabre Lambeau [mailto:fabre(_dot_)lambeau(_at_)gmail(_dot_)com]
Sent: 14 May 2010 14:46
To: xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com
Subject: [xsl] Re: Dynamic number of sort key component?

I'm writing a tool that allows my users to query an XML
document a-la-SQL (but simplified).
For example, given an XML document such as:

<table>
  <row>
    <col name="a">1</col>
    <col name="b">2</col>
    <col name="c">3</col>
    <col name="d">4</col>
  </row>
  <row>
    <col name="a">bla</col>
    <col name="b">bli</col>
    <col name="c">blo</col>
    <col name="d">blu</col>
  </row>
  <!-- ... -->
</table>

I'd like them to be able to say something like:
SELECT a,b,c,d
ORDER BY a ASC,b DESC
GROUP BY a,c,d

My component (XSLT stylesheet) takes that query and
effectively processes it in XSLT.
The GROUP BY statement is easy to process, since I can just
concatenate the values for the corresponding <col> elements,
and use that string as a key to a for-each-group.
However, I can't find a way to do the sorting (for which the
order is different for each individual column).

What I seem to need is the ability to have a dynamic number
of sort key components, which as far as I know is not
possible The following is obviously not possible:
<xsl:perform-sort select="col">
  <xsl:for-each select="tokenize($group-by, ',')">
    <xsl:sort select="col[(_at_)name=string-before(current(),' '])
order="{string-after(current(),' ']}ending"/>
  </xsl:for-each>
</xsl:perform>

Can anyone think of a way to do this (whilst retaining the
ability to have a dynamic list of ORDER BY columns, each one
specifying its own order)?
Unfortunately, I don't think I have the ability to generate a
separate XSLT sheet after parsing the query string and
execute it, as my engine is all in XSLT (executed with AltovaXML)

--
Fabre Lambeau

--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: 
<mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--



--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: 
<mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--



--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--

<Prev in Thread] Current Thread [Next in Thread>