Re: [xsl] xsl:for-each-group help needed !

Subject: Re: [xsl] xsl:for-each-group help needed !
From: "Graydon graydon@xxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Wed, 16 Sep 2020 22:47:35 -0000
> > On 16.09.2020 23:19, Christophe Marchand cmarchand@xxxxxxxxxx wrote:
[group the blocks]
> Here is a sample input :
> 
> <blocks>
>    <block id="i1">content</block>
>    <block id="i2" width="33">content</block>
>    <block id="i3" width="67">content</block>
>    <block id="i4" width="50">content</block>
>    <block id="i5" width="50">content</block>
>    <block id="i6" width="25">content</block>
>    <block id="i7" width="55">content</block>
>    <block id="i8" width="20">content</block>
>    <block id="i9">content</block>
> </blocks>
> 
> Here is the expected output :
> 
> <blocks>
>    <block id="i1">content</block>
>    <block type="composite">
>      <block id="i2" width="33">content</block>
>      <block id="i3" width="67">content</block>
>    </block>
>    <block type="composite">
>      <block id="i4" width="50">content</block>
>      <block id="i5" width="50">content</block>
>    </block>
>    <block type="composite">
>      <block id="i6" width="25">content</block>
>      <block id="i7" width="55">content</block>
>      <block id="i8" width="20">content</block>
>    </block>
>    <block id="i9">content</block>
> </blocks>
> 
> I have no idea of which form of for-each-group I have to use...

It's not as short as I hoped, but a two-pass approach can avoid an
accumulator:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet exclude-result-prefixes="xs math xd" version="3.0"
  xmlns:math="http://www.w3.org/2005/xpath-functions/math"; xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl";
  xmlns:xs="http://www.w3.org/2001/XMLSchema"; xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
  <xd:doc scope="stylesheet">
    <xd:desc>
      <xd:p><xd:b>Created on:</xd:b> Sep 16, 2020</xd:p>
      <xd:p><xd:b>Author:</xd:b> graydon</xd:p>
      <xd:p />
    </xd:desc>
  </xd:doc>
  <xsl:mode name="numbers" on-no-match="shallow-copy" />
  <xsl:mode name="group" on-no-match="shallow-copy" />
  <xsl:variable as="element(blocks)" name="input">
    <blocks>
      <block id="i1">content</block>
      <block id="i2" width="33">content</block>
      <block id="i3" width="67">content</block>
      <block id="i4" width="50">content</block>
      <block id="i5" width="50">content</block>
      <block id="i6" width="25">content</block>
      <block id="i7" width="55">content</block>
      <block id="i8" width="20">content</block>
      <block id="i9">content</block>
    </blocks>
  </xsl:variable>
  <xd:doc>
    <xd:desc>main template</xd:desc>
  </xd:doc>
  <xsl:template name="xsl:initial-template">
    <xsl:variable name="pass1">
      <xsl:apply-templates mode="numbers" select="$input" />
    </xsl:variable>
    <xsl:apply-templates mode="group" select="$pass1" />
  </xsl:template>
  <xd:doc>
    <xd:desc>add numbers to the blocks</xd:desc>
  </xd:doc>
  <xsl:template match="block" mode="numbers">
    <xsl:copy>
      <xsl:apply-templates mode="numbers" select="@*" />
      <xsl:if test="@width">
        <xsl:attribute name="count" select="(preceding-sibling::block/@width => sum()) mod 100" />
      </xsl:if>
      <xsl:apply-templates mode="numbers" select="node()" />
    </xsl:copy>
  </xsl:template>
  <xd:doc>
    <xd:desc>do the grouping</xd:desc>
  </xd:doc>
  <xsl:template match="blocks" mode="group">
    <xsl:copy>
      <xsl:apply-templates mode="group" select="@*" />
      <xsl:for-each-group group-starting-with="block[@count/number() lt preceding-sibling::block[1]/@count/number()]"
        select="*">
        <xsl:for-each-group group-adjacent="(self::block[exists(@count)], false())[1]" select="current-group()">
          <xsl:choose>
            <xsl:when test="current-grouping-key()">
              <block type="composite">
                <xsl:apply-templates mode="group" select="current-group()" />
              </block>
            </xsl:when>
            <xsl:otherwise>
              <xsl:apply-templates mode="group" select="current-group()" />
            </xsl:otherwise>
          </xsl:choose>
        </xsl:for-each-group>
      </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>
  <xd:doc>
    <xd:desc>remove count attributes</xd:desc>
  </xd:doc>
  <xsl:template match="@count" mode="group">
    <!-- thud -->
  </xsl:template>
</xsl:stylesheet>

produces:

<?xml version="1.0" encoding="UTF-8"?>
<blocks>
  <block id="i1">content</block>
  <block type="composite">
    <block id="i2" width="33">content</block>
    <block id="i3" width="67">content</block>
  </block>
  <block type="composite">
    <block id="i4" width="50">content</block>
    <block id="i5" width="50">content</block>
  </block>
  <block type="composite">
    <block id="i6" width="25">content</block>
    <block id="i7" width="55">content</block>
    <block id="i8" width="20">content</block>
  </block>
  <block id="i9">content</block>
</blocks>


-- 
Graydon Saunders  | graydonish@xxxxxxxxx
^fs oferiode, pisses swa mfg.
-- Deor  ("That passed, so may this.")

Current Thread