VextoR
VextoR

Reputation: 5165

Counting same values in xml using XSLT

I want to add <LineNum>[number]</LineNum> in output and LineNum has to start from '1' again if we have different value in <cell num="4">

Source xml:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<table>
   <sheet name="Notification" num="1">
      <row num="1">
         <cell num="1">IBD</cell>
         <cell num="2">YES</cell>
         <cell num="3">6600027830(010000)</cell>
         <cell num="4">800015001</cell>
      </row>
      <row num="2">
         <cell num="1">IBD</cell>
         <cell num="2">YES</cell>
         <cell num="3">6600027830(010000)</cell>
         <cell num="4">800015001</cell>
      </row>
      <row num="3">
         <cell num="1">IBD</cell>
         <cell num="2">YES</cell>
         <cell num="3">6600027831(010000)</cell>
         <cell num="4">800015002</cell>
      </row>
      <row num="4">
         <cell num="1">IBD</cell>
         <cell num="2">YES</cell>
         <cell num="3">6600027831(010000)</cell>
         <cell num="4">800015002</cell>
      </row>
      <row num="5">
         <cell num="1">IBD</cell>
         <cell num="2">YES</cell>
         <cell num="3">6600027831(010000)</cell>
         <cell num="4">800015002</cell>
      </row>
   </sheet>
</table>

so will be in this case:

<cell num="4">800015001</cell>
<LineNum>1</LineNum>
<cell num="4">800015001</cell>
<LineNum>2</LineNum>

<!-- cell num="4" different from previous, start LineNum from 1 -->
<cell num="4">800015002</cell>
<LineNum>1</LineNum>
<cell num="4">800015002</cell>
<LineNum>2</LineNum>
<cell num="4">800015002</cell>
<LineNum>3</LineNum>

I could add global var, assign cell num="4" to it and then check if current value is same as global var, if not - start LineNum from 1 again and reassign global var. Sadly XSLT doesn't provide reassigning.

What can I do?

UPD: was trying to somehow modify example from other question XSL: Counting Previous Unique Siblings, but no luck..

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <!-- don't copy whitespace -->
  <xsl:template match="text()"/>

  <xsl:template match="row/cell[@num=4]">
    <xsl:variable name="roles-so-far" select=". | preceding::row/cell[@num=4]"/>
    <xsl:value-of select="count($roles-so-far)"/>
    <xsl:value-of select="' '"/>
    <xsl:value-of select="."/>

    <!-- Only select the first instance of each ROLE name -->
    <xsl:variable name="roles-so-far-unique"
                  select="$roles-so-far[not(. = preceding-sibling::row/cell[@num=4])]"/>

    <xsl:apply-templates select="/"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="count($roles-so-far-unique)"/>
    <xsl:text>&#xA;</xsl:text> <!-- linefeed -->


  </xsl:template>

</xsl:stylesheet>

Upvotes: 0

Views: 385

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167401

With the question being tagged as xslt-2.0 you can easily use for-each-group group-adjacent:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="xs"
  version="2.0">

<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>

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

<xsl:template match="sheet">
  <xsl:copy>
    <xsl:for-each-group select="row" group-adjacent="cell[@num = 4]">
      <xsl:apply-templates select="current-group()"/>
    </xsl:for-each-group>
  </xsl:copy>
</xsl:template>

<xsl:template match="row">
  <xsl:copy>
    <xsl:apply-templates select="@* , node()"/>
    <LineNum><xsl:value-of select="position()"/></LineNum>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>

When I apply above code with Saxon 9.4 to your input sample I get the following result:

<table>
   <sheet>
      <row num="1">
         <cell num="1">IBD</cell>
         <cell num="2">YES</cell>
         <cell num="3">6600027830(010000)</cell>
         <cell num="4">800015001</cell>
         <LineNum>1</LineNum>
      </row>
      <row num="2">
         <cell num="1">IBD</cell>
         <cell num="2">YES</cell>
         <cell num="3">6600027830(010000)</cell>
         <cell num="4">800015001</cell>
         <LineNum>2</LineNum>
      </row>
      <row num="3">
         <cell num="1">IBD</cell>
         <cell num="2">YES</cell>
         <cell num="3">6600027831(010000)</cell>
         <cell num="4">800015002</cell>
         <LineNum>1</LineNum>
      </row>
      <row num="4">
         <cell num="1">IBD</cell>
         <cell num="2">YES</cell>
         <cell num="3">6600027831(010000)</cell>
         <cell num="4">800015002</cell>
         <LineNum>2</LineNum>
      </row>
      <row num="5">
         <cell num="1">IBD</cell>
         <cell num="2">YES</cell>
         <cell num="3">6600027831(010000)</cell>
         <cell num="4">800015002</cell>
         <LineNum>3</LineNum>
      </row>
   </sheet>
</table>

Upvotes: 1

Related Questions