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:
group-by(Value-Based Grouping)
Groups items based on an evaluated expression, similar to aGROUP BYclause 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>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.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.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
- Readability and Maintainability: Code is self-documenting and expresses the grouping intent directly without boilerplate keys.
- Support for Atomic Values: In addition to node
sequences,
xsl:for-each-groupcan group atomic values like strings, dates, or numbers. - Engine Optimization: XSLT processors can optimize native grouping operations internally, often yielding better execution speed and memory efficiency compared to complex legacy templates.
- Versatility: By supporting both value-based and positional/document-order grouping, it effortlessly handles complex content-structuring tasks that were previously impractical.