Re: [xsl] Feedback on grouping solution

Subject: Re: [xsl] Feedback on grouping solution
From: "Dimitre Novatchev dnovatchev@xxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Sun, 27 Oct 2019 01:24:16 -0000
Actually, we don't need 2-pass processing!

Below is a really short and simple *XSLT 1* transformation -- it is just 26
lines -- *40% shorter* than the XSLT 3.0 transformation provided by Martin.

Enjoy:

<xsl:stylesheet version="1.0" xmlns:xsl="
http://www.w3.org/1999/XSL/Transform";>
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

  <xsl:template match="node()|@*" name="identity">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="step"><xsl:apply-templates
select="*[1]"/></xsl:template>

  <xsl:template match="step/*">
    <xsl:param name="pAdjustments" select="0"/>
    <xsl:variable name="vNeedsAdjustment" select="self::figure and
(count(preceding-sibling::*) +1 + $pAdjustments) mod 2 = 1"/>

    <xsl:if test="$vNeedsAdjustment">
      <spacer/>
    </xsl:if>
    <xsl:call-template name="identity"/>

    <xsl:apply-templates select="following-sibling::*[1]">
      <xsl:with-param name="pAdjustments" select="$pAdjustments +
$vNeedsAdjustment"/>
    </xsl:apply-templates>
  </xsl:template>
</xsl:stylesheet>

Cheers,
Dimitre


On Sat, Oct 26, 2019 at 4:22 PM Dimitre Novatchev dnovatchev@xxxxxxxxx <
xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote:

> > Now waiting for Dimitre posting a less "fancy"
> > but equally compact XSLT 1 solution :)
>
> The following is a 48-lines XSLT 2.0 solution (2 passes -- equivalent XSLT
> 1.0 solution is easy to produce but will need the vendor:node-set()
> extension function).
>
> Martin's XSLT 3.0 solution is 43 lines when <xsl:stylesheet> and
> <xsl:output> are added. So the difference in number of lines is not big --
> 12% and I believe the XSLT 2 solution below is less complex and more easily
> understandable.
>
> Maybe I am biased, but I personally believe that it is better to use the
> standard XPath 3 functions fold-left() and fold-right() in the spirit of
> functional programming. <xsl:iterate> instead helps people avoid thinking
> using the concepts of functional programming. Also, its definition is so
> complex that I personally needed more than an hour to find out / construct
> what cases of its usage are possible and which are mutually exclusive.
>
> Here is the transformation:
>
> <xsl:stylesheet version="2.0" xmlns:xsl="
> http://www.w3.org/1999/XSL/Transform";>
>  <xsl:output omit-xml-declaration="yes" indent="yes"/>
>  <xsl:strip-space elements="*"/>
>
>   <xsl:variable name="vPass1Result"><xsl:apply-templates
> select="/*"/></xsl:variable>
>
>   <xsl:template match="node()|@*" mode="#default pass2">
>     <xsl:copy>
>       <xsl:apply-templates select="node()|(@* except @stepChild)"
> mode="#current"/>
>     </xsl:copy>
>   </xsl:template>
>
>   <xsl:template match="/">
>     <xsl:apply-templates select="$vPass1Result/*" mode="pass2"/>
>   </xsl:template>
>
>   <xsl:template match="step"><xsl:apply-templates/></xsl:template>
>
>   <xsl:template match="step/*">
>     <xsl:copy>
>       <xsl:apply-templates select="@*"/>
>       <xsl:attribute name="stepChild">
>         <xsl:number level="any" count="/*/step/*"/>
>       </xsl:attribute>
>       <xsl:apply-templates select="node()"/>
>     </xsl:copy>
>   </xsl:template>
>
>   <xsl:template match="/*" mode="pass2">
>    <xsl:copy>
>      <xsl:apply-templates select="*[1]" mode="pass2"/>
>    </xsl:copy>
>   </xsl:template>
>
>   <xsl:template match="/*/*" mode="pass2">
>     <xsl:param name="pAdjustments" select="0"/>
>     <xsl:variable name="vNeedsAdjustment" select="self::figure and
> (count(preceding-sibling::*[@stepChild]) +1 + $pAdjustments) mod 2 = 1"/>
>
>     <xsl:if test="$vNeedsAdjustment">
>       <spacer/>
>     </xsl:if>
>     <xsl:next-match/>
>
>     <xsl:apply-templates select="following-sibling::*[1]" mode="pass2">
>       <xsl:with-param name="pAdjustments" select="$pAdjustments +
> (if($vNeedsAdjustment) then 1 else 0)"/>
>     </xsl:apply-templates>
>   </xsl:template>
> </xsl:stylesheet>
>
>
> Cheers,
> Dimitre
>
>
>
> On Sat, Oct 26, 2019 at 12:06 PM Martin Honnen martin.honnen@xxxxxx <
> xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote:
>
>> On 26.10.2019 19:03, Rick Quatro rick@xxxxxxxxxxxxxx wrote:
>>
>> > I need to process the <step> child elements so that the <figure>
>> > elements are always on the "right" (even-numbered position) in the
>> > output. Immediate children of the <procedure> do not factor into the
>> > odd/even sequence.
>> >
>> > The first child of each group of adjacent <step> elements starts a new
>> > odd/even series. To ensure that the each <figure> is in an even-numbered
>> > position, I want to insert a <spacer> element where it is required.
>>
>> > Here is my stylesheet. My basic question is: is there a better or more
>> > efficient way to do this?
>>
>> I think with XSLT 3 it is possible to use xsl:iterate on the child
>> elements of the adjacent steps found by grouping:
>>
>>      <xsl:mode on-no-match="shallow-copy"/>
>>
>>      <xsl:template match="procedure">
>>          <xsl:copy>
>>              <xsl:for-each-group select="*"
>> group-adjacent="boolean(self::step)">
>>                  <xsl:choose>
>>                      <xsl:when test="current-grouping-key()">
>>                          <xsl:iterate select="current-group()/*">
>>                              <xsl:param name="position-in-output"
>> select="1"/>
>>                              <xsl:apply-templates select=".">
>>                                  <xsl:with-param
>> name="position-in-output" select="$position-in-output"/>
>>                              </xsl:apply-templates>
>>                              <xsl:next-iteration>
>>                                  <xsl:with-param name="position-in-output"
>>                                      select="if (self::figure and
>> $position-in-output mod 2 = 1)
>>                                              then $position-in-output + 2
>>                                              else $position-in-output +
>> 1"/>
>>                              </xsl:next-iteration>
>>                          </xsl:iterate>
>>                      </xsl:when>
>>                      <xsl:otherwise>
>>                          <xsl:apply-templates select="current-group()"/>
>>                      </xsl:otherwise>
>>                  </xsl:choose>
>>              </xsl:for-each-group>
>>          </xsl:copy>
>>      </xsl:template>
>>
>>      <xsl:template match="step/figure">
>>          <xsl:param name="position-in-output"/>
>>          <xsl:if test="$position-in-output mod 2 = 1">
>>              <spacer/>
>>          </xsl:if>
>>          <xsl:next-match/>
>>      </xsl:template>
>>
>>
>>
>> Now waiting for Dimitre posting a less "fancy" but equally compact XSLT
>> 1 solution :)

Current Thread