2

Consider the following XML structure:

<a>
<b>
    <c>
        <d x="1"/>
        <d x="2"/>
        <d x="3"/>
        <d x="3"/>
        <d x="4"/>
    </c>
</b>
<b>
    <c>
        <d x="1"/>
        <d x="1"/>
        <d x="2"/>
        <d x="3"/>
        <d x="4"/>
        <d x="5"/>
        <d x="5"/>
    </c>
</b>
<b>
    <c>
        <d x="1"/>
        <d x="2"/>
        <d x="3"/>
        <d x="3"/>
        <d x="4"/>
        <d x="5"/>
        <d x="5"/>
    </c>
</b>

I'd like a XPath 1.0 statement to give me the minimum and maximum values of @x? So far I have the following for the minimum:

//a/b/c/d[not(preceding-sibling::d/@x <= @x) and not(following-sibling::d/@x <= @x)]/@x

which is close, but no cigar :-(

Any help greatly appreciated!

Thanks, J

redmamoth
  • 55
  • 1
  • 2
  • 8
  • If you're doing this purely out of curiosity that's fine but I would highly recommend against using XPath to do this as it scores pretty low on code readability. – Spencer Ruport Feb 27 '13 at 16:53
  • Thanks, I'm just seeing what I can feasably do within the XPath, as i'm trying to provide my solution within a single XSLT that will then be used to display the XML in a readable format. I guess this won't be very good performance wise either, considering there could be 100's of @x's!? – redmamoth Feb 28 '13 at 08:38

2 Answers2

11

To fetch the maximum value, look for all attribute values for which there are not smaller ones. If there are multiple results, take the first one - they must be equal.

(//@x[not(. < //@x)])[1]

For the minimum value, just replace < by >.

For completeness reasons: if your XPath engine supports XPath 2.0 (or better), just use max(//@x) respecting min(//@x) which will probably be faster and more readable.

Jens Erat
  • 37,523
  • 16
  • 80
  • 96
  • Wow, even simpler than I thought, thanks! Yeah, unfortunately i'm working in XPath 1.0. – redmamoth Feb 28 '13 at 08:23
  • 1
    This works well when @x is an integer, but now I have another attribute which contains a string to represent a date, in the format YYYY-MM. I want to get the earliest year. I've tried wrapping number(substring(.,1,4)) around the attributes, but no dice. Any ideas for this scenario? – redmamoth Feb 28 '13 at 09:05
0

I solved similar problem, where I processed <b> and <c> elements in individual nested for-each cycles and I wanted get of the content of <d> the first element with maximum x value in each section. I used the following code:

<xsl:for-each select="/a/b">
  <!-- do something -->
  <xsl:for-each select="./c">
    <!-- do something -->
    <xsl:value-of select="((./d/@x[not(. &lt; ../../d/@x)])[1])/../." /> 
  </xsl:for-each>
</xsl:for-each>
Petr Voborník
  • 1,249
  • 1
  • 14
  • 11