Re: [xsl] how to make a group-by multiple attributes motionless

Subject: Re: [xsl] how to make a group-by multiple attributes motionless
From: "Michael Kay mike@xxxxxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Wed, 25 Sep 2019 22:55:44 -0000
This code passes Saxon's streamability tests:

<xsl:stylesheet version="3.0" xmlns:xs="";

  <xsl:mode streamable="yes"/>

  <xsl:template match="/">
        <xsl:for-each-group select="/*/row" group-by="((@* => copy-of() =>
sort((), function($x){name($x)})) ! (name() || '=' || string(.))) =>
string-join(' ')">
          <xsl:sequence select="current-group()"/>

Note that the sort() function requires XPath 3.1 so its streamability analysis
isn't covered in the XSLT 3.0 spec, but Saxon applies similar rules to
functions such as fn:filter: if the first argument is grounded then the
function is streamable.

The key here is the use of copy-of() to copy the attributes before sorting;
this enables the compiler to know that the sort function isn't going to do any
non-streamable navigation starting at the attribute node. In fact, after
sorting you could equally well call a user-defined function that sorts using

Michael Kay

> On 25 Sep 2019, at 22:44, Geert Bormans geert@xxxxxxxxxxxxxxxxxxx
<xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote:
> All,
> I a streaming XSLT 3.0, I have to group a series of elements by their
attribute names and values
> I have issues making the group-by motionless
> In a simplified example
> <rows>
>     <row a="val-a-1" b="b-val"/>
>     <row a="val-a-2" b="b-val"/>
>     <row a="val-a-1" b="b-val"/>
>     <row a="val-a-2"/>
> </rows>
> I need grouping this way
> <rowgroups>
>    <rowgroup hash="|a=val-a-1|b=b-val"/>
>    <rowgroup hash="|a=val-a-2|b=b-val"/>
>    <rowgroup hash="|a=val-a-2"/>
> </rowgroups>
> easily achieved this way
>              <xsl:fork>
>                 <xsl:for-each-group select="*"
group-by="string-join(@*/concat('|', name(), '=', .), '')">
>                     <rowgroup hash="{current-grouping-key()}"/>
>                 </xsl:for-each-group>
>             </xsl:fork>
> but, I could have data like this (third line attribute order swapped)
> <rows>
>     <row a="val-a-1" b="b-val"/>
>     <row a="val-a-2" b="b-val"/>
>     <row b="b-val" a="val-a-1"/>
>     <row a="val-a-2"/>
> </rows>
> and this basically gives me a fourth group using the code above
> So I want to make sure that the attributes order can be controlled when I
prepare the group-by, but any sorting attempt I make, leads to the group-by no
longer being motionless
> I would welcome your suggestions
> Thanks
> Met vriendelijke groeten,
> Best regards,
> Geert Bormans
> XSL-List info and archive <>
> EasyUnsubscribe <> (by
email <>)

Current Thread