See the XML code of a table below. My task is to add an attribute (MOREROWS) to every ENTRY element which has one or more consecutive following rows with the text "##rowspan##" in the ENTRY element with the same COLNAME value.
The MOREROWS attribute should contain the number of following consecutive rows satisfying the desired condition.
I've been experimenting with for-each-group, for-each and count, but haven't managed to find a solution yet. My main problem is to ignore the gaps. After all I want to count only the consecutive following rows satisfying the condition, and not all the following rows satisfying the condition.
Ideally I would want to use count as that seems like the logical choice here since I don't need to apply templates but rather just want a number.
Any help will be appreciated.
Current table:
<TABLE>
<ROW>
<ENTRY COLNUM="1" COLNAME="col1">
<CONTENT>42</CONTENT>
</ENTRY>
<ENTRY COLNUM="2" COLNAME="col2">
<CONTENT>155</CONTENT>
</ENTRY>
<ENTRY COLNUM="3" COLNAME="col3">
<CONTENT/>
</ENTRY>
<ENTRY COLNUM="4" COLNAME="col4">
<CONTENT>148</CONTENT>
</ENTRY>
</ROW>
<ROW>
<ENTRY COLNUM="1" COLNAME="col1">
<CONTENT>147</CONTENT>
</ENTRY>
<ENTRY COLNUM="2" COLNAME="col2">
<CONTENT>##rowspan##</CONTENT>
</ENTRY>
<ENTRY COLNUM="3" COLNAME="col3">
<CONTENT/>
</ENTRY>
<ENTRY COLNUM="4" COLNAME="col4">
<CONTENT>##rowspan##</CONTENT>
</ENTRY>
</ROW>
<ROW>
<ENTRY COLNUM="1" COLNAME="col1">
<CONTENT>147</CONTENT>
</ENTRY>
<ENTRY COLNUM="2" COLNAME="col2">
<CONTENT>##rowspan##</CONTENT>
</ENTRY>
<ENTRY COLNUM="3" COLNAME="col3">
<CONTENT/>
</ENTRY>
<ENTRY COLNUM="4" COLNAME="col4">
<CONTENT>300</CONTENT>
</ENTRY>
</ROW>
<ROW>
<ENTRY COLNUM="1" COLNAME="col1">
<CONTENT>1814</CONTENT>
</ENTRY>
<ENTRY COLNUM="2" COLNAME="col2">
<CONTENT/>
</ENTRY>
<ENTRY COLNUM="3" COLNAME="col3">
<CONTENT/>
</ENTRY>
<ENTRY COLNUM="4" COLNAME="col4">
<CONTENT>1905</CONTENT>
</ENTRY>
</ROW>
<ROW>
<ENTRY COLNUM="1" COLNAME="col1">
<CONTENT>147</CONTENT>
</ENTRY>
<ENTRY COLNUM="2" COLNAME="col2">
<CONTENT>##rowspan##</CONTENT>
</ENTRY>
<ENTRY COLNUM="3" COLNAME="col3">
<CONTENT/>
</ENTRY>
<ENTRY COLNUM="4" COLNAME="col4">
<CONTENT>##rowspan##</CONTENT>
</ENTRY>
</ROW>
</TABLE>
Desired result:
<TABLE>
<ROW>
<ENTRY COLNUM="1" COLNAME="col1">
<CONTENT>42</CONTENT>
</ENTRY>
<ENTRY COLNUM="2" COLNAME="col2"** MOREROWS="2"**>
<CONTENT>155</CONTENT>
</ENTRY>
<ENTRY COLNUM="3" COLNAME="col3">
<CONTENT/>
</ENTRY>
<ENTRY COLNUM="4" COLNAME="col4"** MOREROWS="1"**>
<CONTENT>148</CONTENT>
</ENTRY>
</ROW>
<ROW>
<ENTRY COLNUM="1" COLNAME="col1">
<CONTENT>147</CONTENT>
</ENTRY>
<ENTRY COLNUM="2" COLNAME="col2">
<CONTENT>##rowspan##</CONTENT>
</ENTRY>
<ENTRY COLNUM="3" COLNAME="col3">
<CONTENT/>
</ENTRY>
<ENTRY COLNUM="4" COLNAME="col4">
<CONTENT>##rowspan##</CONTENT>
</ENTRY>
</ROW>
<ROW>
<ENTRY COLNUM="1" COLNAME="col1">
<CONTENT>147</CONTENT>
</ENTRY>
<ENTRY COLNUM="2" COLNAME="col2">
<CONTENT>##rowspan##</CONTENT>
</ENTRY>
<ENTRY COLNUM="3" COLNAME="col3">
<CONTENT/>
</ENTRY>
<ENTRY COLNUM="4" COLNAME="col4">
<CONTENT>300</CONTENT>
</ENTRY>
</ROW>
<ROW>
<ENTRY COLNUM="1" COLNAME="col1">
<CONTENT>1814</CONTENT>
</ENTRY>
<ENTRY COLNUM="2" COLNAME="col2"** MOREROWS="1"**>
<CONTENT/>
</ENTRY>
<ENTRY COLNUM="3" COLNAME="col3">
<CONTENT/>
</ENTRY>
<ENTRY COLNUM="4" COLNAME="col4"** MOREROWS="1"**>
<CONTENT>1905</CONTENT>
</ENTRY>
</ROW>
<ROW>
<ENTRY COLNUM="1" COLNAME="col1">
<CONTENT>147</CONTENT>
</ENTRY>
<ENTRY COLNUM="2" COLNAME="col2">
<CONTENT>##rowspan##</CONTENT>
</ENTRY>
<ENTRY COLNUM="3" COLNAME="col3">
<CONTENT/>
</ENTRY>
<ENTRY COLNUM="4" COLNAME="col4">
<CONTENT>##rowspan##</CONTENT>
</ENTRY>
</ROW>
</TABLE>
I think this can be solved with xsl:for-each-group group-adjacent e.g. (in XSLT 3)
<xsl:template match="ROW/ENTRY[not(CONTENT = '##rowspan##')]">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:for-each-group select="../following-sibling::ROW/ENTRY[@COLNAME = current()/@COLNAME]" group-adjacent="CONTENT = '##rowspan##'">
<xsl:if test="position() = 1 and current-grouping-key()">
<xsl:attribute name="MOREROWS" select="count(current-group())"/>
</xsl:if>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:mode on-no-match="shallow-copy"/>
It might be a bit more efficient to group once to store the adjacent ENTRYs in a map:
<xsl:variable name="groups" as="map(xs:string, xs:integer)*">
<xsl:map>
<xsl:for-each select="TABLE/ROW[1]/ENTRY">
<xsl:variable name="pos" select="position()"/>
<xsl:for-each-group select="../following-sibling::ROW/ENTRY[$pos]" group-adjacent="CONTENT = '##rowspan##'">
<xsl:if test="current-grouping-key()">
<xsl:map-entry key="current-group()[1]/../preceding-sibling::ROW[1]//ENTRY[$pos]/generate-id()" select="count(current-group())"/>
</xsl:if>
</xsl:for-each-group>
</xsl:for-each>
</xsl:map>
</xsl:variable>
<xsl:template match="ROW/ENTRY[not(CONTENT = '##rowspan##') and map:contains($groups, generate-id(current()))]">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:attribute name="MOREROWS" select="$groups(generate-id(current()))"/>
</xsl:copy>
</xsl:template>
<xsl:mode on-no-match="shallow-copy"/>
To use the map functions you need the stylesheet to declare xmlns:map="http://www.w3.org/2005/xpath-functions/map".