Guide to xsl:for-each-group in XSLT 2.0 and 3.0

The introduction of the xsl:for-each-group instruction in XSLT 2.0 fundamentally transformed how developers organize and aggregate structured data, eliminating the complex workarounds required in XSLT 1.0. This article explains how xsl:for-each-group simplifies XML transformations by replacing legacy techniques like Muenchian grouping with declarative, native grouping mechanisms, exploring its core attributes and practical benefits.

The Problem with Grouping in XSLT 1.0

In XSLT 1.0, there was no built-in instruction for grouping XML nodes. Developers had to rely on the Muenchian grouping method, which required combining <xsl:key>, the key() function, and the generate-id() function. While functional, this approach was non-intuitive, verbose, difficult to maintain, and limited almost exclusively to grouping by distinct values. Positional or sequential grouping remained exceptionally difficult.

The Native Solution: xsl:for-each-group

XSLT 2.0 (and subsequently 3.0) resolved these limitations by introducing <xsl:for-each-group>. This element iterates over a sequence of items, divides them into distinct groups based on a defined criterion, and evaluates its template content once per group.

Within the instruction body, two standard functions provide immediate access to the grouped data: - current-grouping-key(): Returns the key value common to the current group. - current-group(): Returns a sequence containing all items belonging to the current group.

Four Powerful Grouping Modes

The instruction simplifies grouping by supporting four distinct grouping algorithms through specific mutually exclusive attributes:

  1. group-by (Value-Based Grouping)
    Groups items based on an evaluated expression, similar to a GROUP BY clause in SQL. This is the direct, declarative replacement for Muenchian grouping.

    <xsl:for-each-group select="employee" group-by="department">
        <department name="{current-grouping-key()}">
            <xsl:copy-of select="current-group()"/>
        </department>
    </xsl:for-each-group>
  2. group-adjacent (Sequential Value Grouping)
    Creates a new group whenever the value of the grouping expression changes between consecutive sibling nodes. This is ideal for handling runs of identical inline elements or transforming flat documents into nested structures.

  3. group-starting-with (Pattern-Based Start)
    Forms a new group whenever an item matches a specific pattern. It is commonly used when converting flat text or document formats (such as HTML headings followed by paragraphs) into hierarchical sections.

  4. group-ending-with (Pattern-Based End)
    Forms a group that includes all preceding elements up to and including the item matching the specified pattern.

Key Advantages in Modern XSLT