Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to sort, and then pick one item

Tags:

sorting

xml

xslt

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".

like image 969
Jeffrey Roosendaal Avatar asked Oct 06 '22 02:10

Jeffrey Roosendaal


2 Answers

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 nodeset2are 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.

like image 181
Tomalak Avatar answered Oct 10 '22 02:10

Tomalak


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>
like image 29
Reinder Wit Avatar answered Oct 10 '22 04:10

Reinder Wit