Bas Hamer
Bas Hamer

Reputation: 324

how can I get a list of indexes of nodes that have a value using xpath

using the following;

    <a>
  <b>false</b> 
  <b>true</b> 
  <b>false</b> 
  <b>false</b> 
  <b>true</b> 
  </a>

I want to get the following result using something like /a/b[.='true'].position() for a result like 2,5 (as in a collection of the 2 positions)

Upvotes: 1

Views: 65

Answers (2)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243599

I. XPath 1.0 solution:

Use:

count(/*/*[.='true'][1]/preceding-sibling::*)+1

This produces the position of the first b element whose string value is "true":

2

Repeat the evaluation of a similar expression, where [1] is replaced by [2] ,..., etc, up to count(/*/*[.='true'])

XSLT - based verification:

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

 <xsl:template match="/">
   <xsl:for-each select="/*/*[.='true']">
    <xsl:variable name="vPos" select="position()"/>

    <xsl:value-of select=
     "count(/*/*[.='true'][$vPos]
               /preceding-sibling::*) +1"/>
    <xsl:text>&#xA;</xsl:text>
   </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document:

<a>
    <b>false</b>
    <b>true</b>
    <b>false</b>
    <b>false</b>
    <b>true</b>
</a>

The XPath expression is constructed and evaluated for everyb, whose string value is"true". The results of these evaluations are copied to the output:

2
5

II. XPath 2.0 solution:

Use:

index-of(/*/*, 'true')

XSLT 2.0 - based verification:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:template match="/">
   <xsl:sequence select="index-of(/*/*, 'true')"/>
 </xsl:template>
</xsl:stylesheet>

When this XSLT 2.0 transformation is applied on the same XML document (above), the XPath 2.0 expression is evaluated and the result of this evaluation is copied to the output:

2 5

Upvotes: 1

Gilles Qu&#233;not
Gilles Qu&#233;not

Reputation: 185790

A basic (& working) approach in language :

from lxml import etree
root = etree.XML("""
<a>
  <b>false</b> 
  <b>true</b> 
  <b>false</b> 
  <b>false</b> 
  <b>true</b> 
</a>
""")

c = 0
lst = []

for i in root.xpath('/a/b/text()'):
    c+=1
    if i == 'true':
        lst.append(str(c))

print ",".join(lst)

Upvotes: 0

Related Questions