Subject: Re: [xsl] XSLT3 generic grouping functions From: "Michael Kay mike@xxxxxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> Date: Wed, 8 Jul 2020 10:20:52 -0000 |
It might be useful to think of xsl:for-each-group as providing a solution for "stereotype" grouping use cases that are commonly encountered, but looking to other functional primitives for solutions to the more general problem: for example you can use fn:fold-left to build up the set of groups e.g. as an array of arrays. For the common use-case tackled by group-by, creating an index in the form of a map is often a convenient alternative to xsl:for-each-group. If you're starting with the requirement "create a sequence of groups, starting a new group at item x when $test(x) returns true" then rather than use xsl:for-each-group with a group-starting pattern, another approach (producing a sequence of arrays) would be fold-left($population, (), function($groups, $x) {if ($test($x)) then ([$x], $groups) else array:append(head($groups), $x))}) => reverse() (It builds the list of groups in reverse order for convenience and then reverses it at the end). At what point you stop using the "stereotype" xsl:for-each-group instruction and start rolling-your-own is an open question, but with maps, arrays, and higher-order-functions, rolling your own is much more viable than it was. Michael Kay Saxonica > On 8 Jul 2020, at 10:24, Matthieu RICAUD-DUSSARGET m.ricaud-dussarget@xxxxxxxxxxxxxxxxxx <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote: > > Hi all, > > While teaching XSLT, someone ask if it was possible to have a kind of generic function to group XML elements. > I first answer the only way to do it was to use (eventually nested) <xsl:for-each-group> instruction with specific conditions according to the data. > Later, as I was discovering XSLT3, I realize the grouping condition might be sent as an higher-order-function parameter, which lead me to write some generic grouping functions to wrap XML elements (see example below). > > The original code can be found on github at https://github.com/ELSGestion/els-sie-xsl-lib/blob/master/src/main/xsl/els-co mmon_xml.xsl <https://github.com/ELSGestion/els-sie-xsl-lib/blob/master/src/main/xsl/els-c ommon_xml.xsl> > Therebs also some generic xsl to nest titles (https://github.com/ELSGestion/els-sie-xsl-lib/blob/master/src/main/xsl/nest- titles.xsl <https://github.com/ELSGestion/els-sie-xsl-lib/blob/master/src/main/xsl/nest- titles.xsl>) and a specific implementation of it to nest HTML titles (https://github.com/ELSGestion/els-sie-xsl-lib/blob/master/src/main/xsl/nest- html-titles.xsl <https://github.com/ELSGestion/els-sie-xsl-lib/blob/master/src/main/xsl/nest- html-titles.xsl>) > Working examples can be found in the test folder https://github.com/ELSGestion/els-sie-xsl-lib/tree/master/src/test <https://github.com/ELSGestion/els-sie-xsl-lib/tree/master/src/test>, as xspec unit test or xspec driven integration tests. > > I actually have a bug with Saxon 9.9 (https://saxonica.plan.io/issues/4636 <https://saxonica.plan.io/issues/4636>) on the generic starting-with grouping function, but this is an occasion to share theses grouping libraries J > > Any comments, feedbacks or similar code implementation are welcome ! > > Cheers > Matthieu Ricaud-Dussarget > > Example of generic adjacent grouping function used to define a specific bby-name groupingb implementation (therebs a similar set of functions for starting-with) : > > <xd:doc> > <xd:desc> > <xd:p>Wrap adjacent elements into a new "wrapper" element</xd:p> > <xd:p>CAUTION : any text, pi, comments within context will be loose</xd:p> > </xd:desc> > <xd:param name="context">Parent of the adjacent elements to wrap</xd:param> > <xd:param name="adjacent.function">Xpath function to set the adjacency condition</xd:param> > <xd:param name="wrapper">Element wrapper</xd:param> > <xd:param name="keep-context">Say if the context should be kept or not in the result</xd:param> > <xd:return>context (or its content) with wrapped adjacent element</xd:return> > </xd:doc> > <xsl:function name="els:wrap-elements-adjacent" as="node()*"> > <xsl:param name="context" as="element()"/> > <xsl:param name="adjacent.function"/> <!--as="xs:string"--> > <xsl:param name="wrapper" as="element()"/> > <xsl:param name="keep-context" as="xs:boolean"/> > <xsl:variable name="content" as="item()*"> > <xsl:for-each-group select="$context/*" group-adjacent="$adjacent.function(.)"> > <xsl:choose> > <xsl:when test="current-grouping-key()"> > <xsl:for-each select="$wrapper"> > <xsl:copy> > <xsl:copy-of select="@*"/> > <xsl:sequence select="current-group()"/> > </xsl:copy> > </xsl:for-each> > </xsl:when> > <xsl:otherwise> > <xsl:copy-of select="current-group()"/> > </xsl:otherwise> > </xsl:choose> > </xsl:for-each-group> > </xsl:variable> > <xsl:choose> > <xsl:when test="$keep-context"> > <xsl:for-each select="$context"> > <xsl:copy> > <xsl:copy-of select="@*"/> > <xsl:sequence select="$content"/> > </xsl:copy> > </xsl:for-each> > </xsl:when> > <xsl:otherwise> > <xsl:sequence select="$content"/> > </xsl:otherwise> > </xsl:choose> > </xsl:function> > > <xd:doc> > <xd:desc> > <xd:p>Wrap "adjacent by name" elements into a new "wrapper" element</xd:p> > <xd:p>CAUTION : any text, pi, comment within context will be loose</xd:p> > </xd:desc> > <xd:param name="context">Parent of the adjacent elements to wrap</xd:param> > <xd:param name="adjacent.names">sequence of qualified names to set adjacent elements</xd:param> > <xd:param name="wrapper">element wrapper</xd:param> > <xd:param name="keep-context">Say if the context should be kept or not in the result</xd:param> > <xd:return>context (or its content) with wrapped adjacent element</xd:return> > </xd:doc> > <xsl:function name="els:wrap-elements-adjacent-by-names" as="node()*"> > <xsl:param name="context" as="element()"/> > <xsl:param name="adjacent.names" as="xs:string+"/> > <xsl:param name="wrapper" as="element()"/> > <xsl:param name="keep-context" as="xs:boolean"/> > <xsl:sequence select="els:wrap-elements-adjacent( > $context, > function($e) as xs:boolean {name($e) = $adjacent.names}, > $wrapper, > $keep-context)"/> > </xsl:function> > > XSL-List info and archive <http://www.mulberrytech.com/xsl/xsl-list> > EasyUnsubscribe <http://lists.mulberrytech.com/unsub/xsl-list/293509> (by email <>)
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
Re: [xsl] XSLT3 generic grouping fu, Matthieu RICAUD-DUSS | Thread | Re: [xsl] XSLT3 generic grouping fu, Matthieu RICAUD-DUSS |
Re: [xsl] XSLT3 generic grouping fu, Martin Honnen martin | Date | Re: [xsl] XSLT3 generic grouping fu, Matthieu RICAUD-DUSS |
Month |