Jak Balikai
Jak Balikai

Reputation: 81

How to count preceding sibling count until certain condition is fulfilled?

I have following XML .. I want to get the count of 'step' nodes from current 'step' node to the preceding step which has node event with value 'DoubleClick'.

<?xml version="1.0" encoding="utf-8"?>
<gps> 
  <step>
    <event>DoubleClick</event>
  </step>    
  <step>
    <event>click</event>
  </step>
  <step>
    <event>click</event>
  </step>  
  <step>
    <event>click</event>
  </step>
  <step>
    <event>DoubleClick</event>
  </step>    
  <step>
    <event>click</event>
  </step>      
  <step>
    <event>click</event>
  </step>
  <step>
    <event>click</event>
  </step>
  <step>
    <event>DoubleClick</event>
  </step>  
</gps>

Upvotes: 2

Views: 1849

Answers (2)

Linga Murthy C S
Linga Murthy C S

Reputation: 5432

You can use the following xpath when the current node is step:

count(preceding-sibling::step[generate-id(preceding-sibling::step[event = 'DoubleClick'][1]) = generate-id(current()/preceding-sibling::step[event = 'DoubleClick'][1])])

EDIT: And if you want to count even the step with DoubleClick, use this xpath:

count(preceding-sibling::step[event = 'DoubleClick'][1]) * (count(preceding-sibling::step[generate-id(preceding-sibling::step[event = 'DoubleClick'][1]) = generate-id(current()/preceding-sibling::step[event = 'DoubleClick'][1])]) + 1)

Upvotes: 0

michael.hor257k
michael.hor257k

Reputation: 116993

There are several ways you could accomplish this, here's one:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/gps">
    <xsl:copy>
        <xsl:for-each select="step">
            <xsl:copy>
                <xsl:attribute name="x">
                    <xsl:value-of select="position() - count(preceding-sibling::step[event='DoubleClick'][1]/preceding-sibling::step)" />
                </xsl:attribute>
            <xsl:copy-of select="event" />
            </xsl:copy>
          </xsl:for-each>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Note that while simple to implement, it is a bit CPU-intensive. For a large amount of steps, you may prefer something like:

XSLT 1.0

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

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

<xsl:template match="event">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
    <x>
        <xsl:number from="step[event='DoubleClick']" level="any" />
    </x>
</xsl:template>

</xsl:stylesheet>

Upvotes: 1

Related Questions