I'm using XSLT to get data out of a feed. Currently I use this block of code, which simply picks the first item out of the feed. I changed it a bit so it applies to this sample XML.
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="/">
<xsl:value-of select="catalog/book/author"/>
</xsl:template>
</xsl:stylesheet>
I want to sort the xml by price, and then pick the author associated with the highest priced book. I've tried all kind of things, but I can't seem to figure it out.
The current output is "Gambardella, Matthew", but I need it to be "Galos, Mike".
I want to sort the xml by price, and then pick the author associated with the highest priced book.
FWIW, you can do that with pure XPath, too.
/catalog/book[not(price < /catalog/book/price)]/author
(The predicate reads: "Select any <book>
whose <price>
is not lower than that of any book.")
This would select <author>Galos, Mike</author>
in the sample XML.
Notes
This expression does not select the highest priced book, but all highest-priced books (i.e., it would select two books if there were two with the same price). Use
/catalog/book[not(price < /catalog/book/price)][1]/author
to select exactly one matching book (the first one in document order would be selected).
XPath automatically coerces both operands of a "less/greater than (or equal to)"-type of comparison to numbers. As long as the values of <price>
are directly convertible to numbers, the above expression will succeed.
It must be the inverse logic ("not(lower than any)"), because the opposite ("greater than any") can never be true (whereas "greater than or equal to any" would always be true).
The time complexity of a nodeset1[expression < nodeset2]
operations is:
→ O(count(nodeset1) × count(nodeset2)).
In the above case nodeset1
and nodeset2
are identical, so the effective time complexity is:
→ O(n²).
In other words, it is not the most efficient way to solve this problem (I would say that <xsl:apply-templates>
with <xsl:sort>
is), but on the other hand - it's a one-liner that might very well be fast enough for you.
you can specify an <xsl:sort>
inside apply-templates, like so:
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates select="/catalog/book">
<xsl:sort select="price" order="descending" data-type="number"/>
</xsl:apply-templates>
</body>
</html>
</xsl:template>
and then in your little 'book' template, use the position()
to filter out only the first book node
<xsl:template match="book">
<xsl:if test="position() = 1">
<xsl:value-of select="author"/>
<br/>
<xsl:value-of select="price"/>
</xsl:if>
</xsl:template>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With