Difference in XSLT Grouping Attributes?

XSLT 2.0 introduced the <xsl:for-each-group> instruction to simplify complex grouping tasks that previously required verbose Muenchian grouping techniques in XSLT 1.0. The element provides several distinct attributes to control grouping logic, most notably group-by, group-adjacent, and group-starting-with (along with its counterpart group-ending-with). While all three attributes organize an input sequence into distinct collections processed via current-group() and current-grouping-key(), they differ fundamentally in whether they evaluate items based on global value equality, sequential runs of identical values, or positional patterns within the document structure.

group-by: Value-Based Global Grouping

The group-by attribute groups items by evaluating a key expression for each item across the entire sequence. All items that evaluate to the same key value are placed into the same group, regardless of where they appear in the original source document.

<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: Consecutive Value Grouping

The group-adjacent attribute forms groups strictly from contiguous sequences of items that share the exact same key value. If an item has a different key value than its predecessor, a new group starts immediately, even if that key value appeared earlier in the sequence.

<xsl:for-each-group select="*" group-adjacent="boolean(self::li)">
  <xsl:choose>
    <xsl:when test="current-grouping-key()">
      <ul>
        <xsl:copy-of select="current-group()"/>
      </ul>
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy-of select="current-group()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:for-each-group>

group-starting-with: Pattern-Based Structural Grouping

The group-starting-with attribute uses a pattern (similar to an xsl:template match="..." pattern) rather than an expression. A new group begins whenever an item matches the specified pattern, and that matched item becomes the first element in the new group. All subsequent items belong to this group until another matching item appears.

<xsl:for-each-group select="*" group-starting-with="h2">
  <section>
    <xsl:copy-of select="current-group()"/>
  </section>
</xsl:for-each-group>

Comparison Summary

Attribute Mechanism Handles Non-Adjacent Items current-grouping-key() Available Primary Purpose
group-by Expression evaluation Yes (merges globally) Yes Categorizing and aggregating data
group-adjacent Expression evaluation No (splits on value change) Yes Grouping contiguous sequences
group-starting-with Pattern matching No (splits on pattern match) No Converting flat structures to hierarchies