Sorting and Merging Sequences

xsl:sort (select, order, data-type, collation) repeats for secondary keys in xsl:for-each, xsl:apply-templates or xsl:perform-sort. xsl:merge combines inputs that are already sorted, running xsl:merge-action once per key. XQuery 3.1 FLWOR Expressions's web orders meet app-orders.xml (four sample app orders, 28 September to 2 October); xslt3 -it -xsl:merge.xsl starts at xsl:initial-template:

merge.xsl: merging two sorted feeds into a daily reportXML
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" expand-text="yes">
  <xsl:output method="text"/>
  <xsl:template name="xsl:initial-template">
    <xsl:merge>
      <xsl:merge-source name="web" select="doc('../s2-15/orders.xml')//order">
        <xsl:merge-key select="xs:date(@date)"/>
      </xsl:merge-source>
      <xsl:merge-source name="app" select="doc('app-orders.xml')//order">
        <xsl:merge-key select="xs:date(@date)"/>
      </xsl:merge-source>
      <xsl:merge-action>
        <xsl:text>{current-merge-key()}: {count(current-merge-group('web'))} web, </xsl:text>
        <xsl:text>{count(current-merge-group('app'))} app&#10;</xsl:text>
      </xsl:merge-action>
    </xsl:merge>
  </xsl:template>
</xsl:stylesheet>
Output
2026-09-28: 2 web, 1 app
2026-09-29: 3 web, 0 app
2026-09-30: 2 web, 2 app
2026-10-01: 1 web, 0 app
2026-10-02: 0 web, 1 app

A merge holds one group at a time and checks order: an app order redated out of sequence stopped Saxon 726,956 with XTDE2220 Merge input for source app is not ordered according to merge key.


Sorting and Merging

For simplicity, the examples in this section use sequences of integers and strings; XPath expressions and nodes can be used in the same way. For instance, an XML-based merge source can be written as:
<xsl:merge-source for-each="doc(a.xml), doc(b.xml)" select="a/b/@c">…

This selects the a/b/@c attribute from each document named in the for-each list as a separate merge source.

<xsl:perform-sort>, <xsl:sort>

<xsl:perform-sort> sorts a sequence using one or more <xsl:sort> keys, applied in order.
ch06-perform-sort.xslXML
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="3.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      xmlns:fn="http://www.w3.org/2005/xpath-functions">
  <xsl:template match="/">
     <xsl:perform-sort select="('1a','3b','8b','4b','2','1c','1b')">
        <xsl:sort select="substring(.,1,1)"/>
        <xsl:sort select="substring(.,2,1)"
                      order="descending"/>
     </xsl:perform-sort>
   </xsl:template>
</xsl:transform>
Output
<?xml version="1.0" encoding="UTF-8"?>1c 1b 1a 2 3b 4b 8b
<xsl:sort> can have the following attributes: select (expression), lang ({language}), order ({"ascending" | "descending"}), collation ({uri}), stable ({"yes" | "no"}), case-order ({"upper-first" | "lower-first"}), data-type ({"text" | "number" | eqname}).

<xsl:merge>, <xsl:merge-source>, <xsl:merge-key>, <xsl:merge-action>

<xsl:merge> merges several already-sorted (or sort-on-the-fly) sources into a single sequence, grouping items that share the same merge key. The example below merges three numeric sources on different keys.
ch06-merge.xslXML
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="3.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      xmlns:fn="http://www.w3.org/2005/xpath-functions">
   <xsl:template match="/" name="xsl:initial-template">
     <xsl:merge>
       <xsl:merge-source for-each-item="(1,100)"
                 select="(.*5,.*3,.*1,.*7,.*9)" sort-before-merge="yes">
          <xsl:merge-key select="."/>
       </xsl:merge-source>
       <xsl:merge-source
                 select="(4,10,2,8,6)" sort-before-merge="yes">
          <xsl:merge-key select=". * 10"/>
       </xsl:merge-source>
       <xsl:merge-source select="(5)">
          <xsl:merge-key select=". * 10"/>
       </xsl:merge-source>
       <xsl:merge-action>
         <xsl:value-of select="current-merge-group()"/>
         <xsl:text>&#32;</xsl:text>
         <xsl:value-of select="current-merge-key()"/>
         <xsl:text>&#10;</xsl:text>
       </xsl:merge-action>
     </xsl:merge>
   </xsl:template>
</xsl:transform>
Output
<?xml version="1.0" encoding="UTF-8"?>2 20
4 40
5 50
6 60
8 80
100 100 10 100
300 300 300
500 500 500
700 700 700
900 900 900
<xsl:merge-source> can have the following attributes: bind-source (eqname), streamable ("yes" | "no"), sort-before-merge ("yes" | "no").
<xsl:merge-key> can have the following attributes: lang ({language}), order ({"ascending" | "descending"}), collation ({uri}), case-order ({"upper-first" | "lower-first"}), data-type ({"text" | "number" | eqname}).