XSLT - Grouping Data with <xsl:for-each-group> in XSLT 2.0

The <xsl:for-each-group> instruction is an important feature introduced in XSLT 2.0 for organizing related items into groups before processing them. It is especially useful when an XML document contains repeated records that need to be categorized according to a common value, such as department, country, category, author, or product type.

In earlier XSLT versions, grouping could require more complicated techniques such as Muenchian grouping. XSLT 2.0 provides <xsl:for-each-group> as a more direct and readable way to perform grouping. It selects a sequence of items, divides those items into groups according to specified criteria, and then processes each group separately. The instruction can group nodes by their value, by a calculated expression, by adjacent values, or by starting and ending patterns.

Basic Syntax

A simple group-by operation can be written as:

<xsl:for-each-group select="book" group-by="category">
    <category name="{current-grouping-key()}">
        <xsl:for-each select="current-group()">
            <book>
                <xsl:value-of select="title"/>
            </book>
        </xsl:for-each>
    </category>
</xsl:for-each-group>

Here, select="book" specifies the nodes that need to be grouped. The group-by="category" expression tells XSLT to place books having the same category value into the same group.

Two important functions are commonly used inside <xsl:for-each-group>:

  • current-group() returns all the items belonging to the current group.

  • current-grouping-key() returns the value used to create the current group.

Example XML

Consider the following XML document:

<library>
    <book>
        <title>XML Basics</title>
        <category>Technology</category>
    </book>
    <book>
        <title>Learning Java</title>
        <category>Programming</category>
    </book>
    <book>
        <title>XSLT Guide</title>
        <category>Technology</category>
    </book>
    <book>
        <title>Python Fundamentals</title>
        <category>Programming</category>
    </book>
</library>

Suppose the requirement is to display the books according to their category.

The XSLT can use:

<xsl:for-each-group select="library/book" group-by="category">
    <h2>
        <xsl:value-of select="current-grouping-key()"/>
    </h2>

    <ul>
        <xsl:for-each select="current-group()">
            <li>
                <xsl:value-of select="title"/>
            </li>
        </xsl:for-each>
    </ul>
</xsl:for-each-group>

The transformation produces a structure similar to:

Technology
    XML Basics
    XSLT Guide

Programming
    Learning Java
    Python Fundamentals

The first group contains books whose category is Technology. The second group contains books whose category is Programming.

The group-by Attribute

The group-by attribute is the most commonly used grouping method. It creates groups based on the value returned by an XPath expression.

For example:

<xsl:for-each-group select="employee" group-by="department">

If the source contains employees from the same department, all employees having the same department value will be placed into one group.

For example:

<employee>
    <name>Rahul</name>
    <department>Sales</department>
</employee>

<employee>
    <name>Anita</name>
    <department>HR</department>
</employee>

<employee>
    <name>Vijay</name>
    <department>Sales</department>
</employee>

The Sales employees will belong to one group, while the HR employee will belong to another.

The expression used in group-by does not have to be a simple element name. It can be a more complex XPath expression. This makes it possible to group records according to calculated or transformed values.

Understanding current-group()

The current-group() function provides access to all nodes in the group currently being processed.

For example:

<xsl:for-each-group select="employee" group-by="department">

    <h2>
        <xsl:value-of select="current-grouping-key()"/>
    </h2>

    <xsl:for-each select="current-group()">
        <p>
            <xsl:value-of select="name"/>
        </p>
    </xsl:for-each>

</xsl:for-each-group>

When the current group is Sales, current-group() contains all employees belonging to the Sales department. When processing the next group, it contains the employees belonging to that group.

This allows the stylesheet to first create a heading for the group and then process all members of that group.

Understanding current-grouping-key()

The current-grouping-key() function returns the value that was used to create the current group.

For example:

<xsl:for-each-group select="employee" group-by="department">
    <h2>
        <xsl:value-of select="current-grouping-key()"/>
    </h2>
</xsl:for-each-group>

If the current group represents the Sales department, the function returns:

Sales

If the next group represents HR, it returns:

HR

This is particularly useful for creating group headings, labels, or attributes in the output document.

Different Grouping Methods

<xsl:for-each-group> supports several ways of creating groups.

The most common method is group-by.

<xsl:for-each-group select="item" group-by="category">

Another method is group-adjacent. It groups consecutive items when their grouping expression has the same value.

<xsl:for-each-group select="item" group-adjacent="category">

This differs from group-by because items that have the same value but are separated by other values do not necessarily belong to the same group.

For example, consider categories:

A
A
B
B
A

With group-adjacent, this creates three groups:

A A
B B
A

The final A does not join the first A group because it is not adjacent.

XSLT also provides group-starting-with and group-ending-with, which are useful when groups are determined by patterns in the source sequence rather than simply by equal values.

Sorting Groups

Groups can also be sorted using <xsl:sort>.

For example:

<xsl:for-each-group select="employee" group-by="department">

    <xsl:sort select="current-grouping-key()"/>

    <h2>
        <xsl:value-of select="current-grouping-key()"/>
    </h2>

</xsl:for-each-group>

This sorts the groups according to their grouping key. Individual members can also be sorted by placing another <xsl:sort> inside the processing of current-group().

This makes it possible to produce structured reports such as:

Finance
    Anita
    Rahul

HR
    Deepa
    Vijay

Sales
    Kiran
    Meena

Advantages of <xsl:for-each-group>

The major advantage of <xsl:for-each-group> is that it makes grouping much easier to understand and maintain. Instead of manually identifying the first node of every group and constructing complicated XPath expressions, the stylesheet can explicitly state how the groups should be formed.

It is useful for generating department-wise employee reports, category-wise product lists, author-wise book collections, country-wise customer information, date-based reports, and many other structured outputs.

It also separates the concept of creating groups from the actual processing of the items inside those groups. This makes XSLT code clearer and reduces the complexity normally associated with grouping repeated XML data.

Conclusion

<xsl:for-each-group> is a powerful XSLT 2.0 instruction for organizing related XML nodes into logical groups. Its group-by feature is particularly useful when records need to be combined according to a common value. The current-group() function provides access to the members of the current group, while current-grouping-key() identifies the value that created that group.

By supporting group-by, group-adjacent, group-starting-with, and group-ending-with, XSLT 2.0 provides flexible approaches for handling different grouping requirements. As a result, <xsl:for-each-group> is widely useful for creating organized reports and transforming complex XML data into clear, structured output.