Reputation: 244
<html>
<body>
<table>
<tr>
<th>HeaderA</th>
<th>HeaderB</th>
<th>HeaderC</th>
<th>HeaderD</th>
</tr>
<tr>
<td>ContentA</td>
<td>ContentB</td>
<td>ContentC</td>
<td>ContentD</td>
</tr>
</table>
</body>
</html>
I am looking for the most efficient way to select the content 'td' node based on the heading in the corresponding 'th' node..
My current xPath expression..
/html/body/table/tr/td[count(/html/body/table/tr/th[text() = 'HeaderA']/preceding-sibling::*)+1]
Some questions..
../..
) inside count()
?td[?]
or is count(/preceding-sibling::*)+1
the most efficient?Upvotes: 7
Views: 3600
Reputation: 66781
Harmen's answer is exactly what you need for a pure XPATH solution.
If you are really concerned with performance, then you could define an XSLT key:
<xsl:key name="columns" match="/html/body/table/tr/th" use="text()"/>
and then use the key in your predicate filter:
/html/body/table/tr/td[count(key('columns', 'HeaderC')/preceding-sibling::th)+1]
However, I suspect you probably won't be able to see a measurable difference in performance unless you need to filter on columns a lot (e.g. for-each loops with checks for every row for a really large document).
Upvotes: 2
Reputation: 22446
count()
Here is the code with relative xpath-code inside count()
/html/body/table/tr/td[count(../../tr/th[text()='HeaderC']/preceding-sibling::*)+1]
But well, it is not much shorter... It won't be shorter than this in my opinion:
//td[count(../..//th[text()='HeaderC']/preceding-sibling::*)+1]
Upvotes: 3
Reputation: 45775
I would have left Xpath aside... since I assume it was DOM parsed, I'd use a Map data structure, and match the nodes in either client side or server side (JavaScript / Java) manually.
Seems to me XPath is being streatched beyond its limit here.
Upvotes: 1