NealWalters
NealWalters

Reputation: 18227

XSLT for-each-group group-starting-with tail(current-group())

This is a follow up to Martin Honnen's answer on here.

Here was my attempt at his suggestion: XSLT Fiddle link

In spreadsheet form, the data looks like this:

enter image description here

In XML this is the data input:

<VendorFile xmlns="http://etc/VendorFile">
    <Row xmlns="">
        <Meter>123</Meter>
        <State>TX</State>
        <StartDate/>
        <Volume/>
    </Row>
    <Row xmlns="">
        <Meter></Meter>
        <State></State>
        <StartDate>1/1/2024</StartDate>
        <Volume>100</Volume>
    </Row>
    <Row xmlns="">
        <Meter></Meter>
        <State></State>
        <StartDate>1/2/2024</StartDate>
        <Volume>110</Volume>
    </Row>
    <Row xmlns="">
        <Meter></Meter>
        <State></State>
        <StartDate>1/3/2024</StartDate>
        <Volume>107</Volume>
    </Row>
    <Row xmlns="">
        <Meter>456</Meter>
        <State>OK</State>
        <StartDate/>
        <Volume/>
    </Row>
    <Row xmlns="">
        <Meter></Meter>
        <State></State>
        <StartDate>1/1/2024</StartDate>
        <Volume>200</Volume>
    </Row>
    <Row xmlns="">
        <Meter></Meter>
        <State></State>
        <StartDate>1/2/2024</StartDate>
        <Volume>205</Volume>
    </Row>
    <Row xmlns="">
        <Meter></Meter>
        <State></State>
        <StartDate>1/3/2024</StartDate>
        <Volume>210</Volume>
    </Row>
</VendorFile> 

My attempted XSLT is this (I have included the group tags to help understand):

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="#all"
  expand-text="yes">

  <xsl:output method="html" indent="yes" html-version="5"/>

    <xsl:template match="/">
     <output>
        <xsl:for-each-group select="//Row" group-starting-with="Row[normalize-space(Meter) and normalize-space(State)]">
          <group>
              <xsl:for-each select="current-group()">
                  <!-- tail = Returns all but the first item in a sequence. --> 
                   <xsl:apply-templates select="tail(current-group())"/>
                  </xsl:for-each>
             </group>       
        </xsl:for-each-group>
     <output>    
    </xsl:template>
    
<xsl:template match="Row">
    <Row>
    <!--<xsl:apply-templates select="current-group()[1]!(Meter, State), *"/>-->
      <Meter><xsl:value-of select="current-group()[1]!(Meter)"/></Meter>
      <State><xsl:value-of select="current-group()[1]!(State)"/></State>
      <StartDate><xsl:value-of select="StartDate"/></StartDate>
      <Volume><xsl:value-of select="Volume"/></Volume>
    </Row>
</xsl:template>

    
    <!--
    <xsl:template match="Row">
        <xsl:copy>
            <xsl:apply-templates select="current-group()[1]!(Meter, State), *"/>
        </xsl:copy>
    </xsl:template>
  -->

</xsl:stylesheet>

My current output is not what I want, but it's rather close. It seems that the startdate and volumes are occurring 3 times for each group.

<output>
   <group>
      <Row><Meter>123</Meter><State>TX</State>
         <StartDate>1/1/2024</StartDate>
         <Volume>100</Volume>
      </Row>
      <Row><Meter>123</Meter><State>TX</State>
         <StartDate>1/2/2024</StartDate>
         <Volume>110</Volume>
      </Row>
      <Row><Meter>123</Meter><State>TX</State>
         <StartDate>1/3/2024</StartDate>
         <Volume>107</Volume>
      </Row>
      <Row><Meter>123</Meter><State>TX</State>
         <StartDate>1/1/2024</StartDate>
         <Volume>100</Volume>
      </Row>
      <Row><Meter>123</Meter><State>TX</State>
         <StartDate>1/2/2024</StartDate>
         <Volume>110</Volume>
      </Row>
      <Row><Meter>123</Meter><State>TX</State>
         <StartDate>1/3/2024</StartDate>
         <Volume>107</Volume>
      </Row>
      <Row><Meter>123</Meter><State>TX</State>
         <StartDate>1/1/2024</StartDate>
         <Volume>100</Volume>
      </Row>
      <Row><Meter>123</Meter><State>TX</State>
         <StartDate>1/2/2024</StartDate>
         <Volume>110</Volume>
      </Row>
      <Row><Meter>123</Meter><State>TX</State>
         <StartDate>1/3/2024</StartDate>
         <Volume>107</Volume>
      </Row>
      <Row><Meter>123</Meter><State>TX</State>
         <StartDate>1/1/2024</StartDate>
         <Volume>100</Volume>
      </Row>
      <Row><Meter>123</Meter><State>TX</State>
         <StartDate>1/2/2024</StartDate>
         <Volume>110</Volume>
      </Row>
      <Row><Meter>123</Meter><State>TX</State>
         <StartDate>1/3/2024</StartDate>
         <Volume>107</Volume>
      </Row>
   </group>
   <group>
      <Row><Meter>456</Meter><State>OK</State>
         <StartDate>1/1/2024</StartDate>
         <Volume>200</Volume>
      </Row>
      <Row><Meter>456</Meter><State>OK</State>
         <StartDate>1/2/2024</StartDate>
         <Volume>205</Volume>
      </Row>
      <Row><Meter>456</Meter><State>OK</State>
         <StartDate>1/3/2024</StartDate>
         <Volume>210</Volume>
      </Row>
      <Row><Meter>456</Meter><State>OK</State>
         <StartDate>1/1/2024</StartDate>
         <Volume>200</Volume>
      </Row>
      <Row><Meter>456</Meter><State>OK</State>
         <StartDate>1/2/2024</StartDate>
         <Volume>205</Volume>
      </Row>
      <Row><Meter>456</Meter><State>OK</State>
         <StartDate>1/3/2024</StartDate>
         <Volume>210</Volume>
      </Row>
      <Row><Meter>456</Meter><State>OK</State>
         <StartDate>1/1/2024</StartDate>
         <Volume>200</Volume>
      </Row>
      <Row><Meter>456</Meter><State>OK</State>
         <StartDate>1/2/2024</StartDate>
         <Volume>205</Volume>
      </Row>
      <Row><Meter>456</Meter><State>OK</State>
         <StartDate>1/3/2024</StartDate>
         <Volume>210</Volume>
      </Row>
      <Row><Meter>456</Meter><State>OK</State>
         <StartDate>1/1/2024</StartDate>
         <Volume>200</Volume>
      </Row>
      <Row><Meter>456</Meter><State>OK</State>
         <StartDate>1/2/2024</StartDate>
         <Volume>205</Volume>
      </Row>
      <Row><Meter>456</Meter><State>OK</State>
         <StartDate>1/3/2024</StartDate>
         <Volume>210</Volume>
      </Row>
   </group>
</output>

Upvotes: 0

Views: 95

Answers (2)

NealWalters
NealWalters

Reputation: 18227

Full working solution from Martin's reply above:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="#all"
  expand-text="yes">

  <xsl:output method="html" indent="yes" html-version="5"/>
  <xsl:mode on-no-match="shallow-copy"/>

    <xsl:template match="/">
      <output>
        <xsl:for-each-group select="//Row" group-starting-with="Row[normalize-space(Meter) and normalize-space(State)]">
          <!--group tag can be removed, it was just for understanding/debugging --> 
      <!--<group>-->
          <!-- tail = Returns all but the first item in a sequence. --> 
          <xsl:apply-templates select="tail(current-group())"/>
      <!--</group>-->
        </xsl:for-each-group>
        </output>

    </xsl:template>
    
<xsl:template match="Row">
    <Row>
    <!-- if not using copy, can do this: 
      <Meter><xsl:value-of select="current-group()[1]!(Meter)"/></Meter>
      <State><xsl:value-of select="current-group()[1]!(State)"/></State>
      <StartDate><xsl:value-of select="StartDate"/></StartDate>
      <Volume><xsl:value-of select="Volume"/></Volume>
      -->
    <xsl:copy>
        <xsl:apply-templates select="current-group()[1]!(Meter, State), * except (Meter, State)"/>
    </xsl:copy>
    </Row>
</xsl:template>


</xsl:stylesheet>

XSLT Fiddler: Link

Upvotes: 0

Martin Honnen
Martin Honnen

Reputation: 167716

Your main error is changing

<xsl:for-each-group select="//Row" group-starting-with="Row[normalize-space(Meter) and normalize-space(State)]">
  <xsl:apply-templates select="tail(current-group())"/>
</xsl:for-each-group>

to

    <xsl:for-each-group select="//Row" group-starting-with="Row[normalize-space(Meter) and normalize-space(State)]">
      <group>
          <xsl:for-each select="current-group()">
              <!-- tail = Returns all but the first item in a sequence. --> 
               <xsl:apply-templates select="tail(current-group())"/>
              </xsl:for-each>
         </group>       
    </xsl:for-each-group>

that way you have inserted a for-each which was not suggested but which for each item in the current group processes the tail of the current group.

Note also that I have reviewed the original answer and have improved the templates for Rows to

<xsl:template match="Row">
    <xsl:copy>
        <xsl:apply-templates select="current-group()[1]!(Meter, State), * except (Meter, State)"/>
    </xsl:copy>
</xsl:template>

I would then suggest to add <xsl:mode on-no-match="shallow-copy"/>.

Upvotes: 3

Related Questions