Reputation: 11796
I would like to select pages when one of two attributes (width or height) are distinct.
Here is my XML:
<pages>
<page id="page0001" height="1000" width="960"></page>
<page id="page4931" height="1000" width="960"></page>
<page id="page6342" height="1000" width="961"></page>
<page id="page7995" height="1001" width="961"></page>
<page id="page4461" height="1001" width="960"></page>
<page id="page4690" height="1001" width="960"></page>
<page id="page9001" height="1001" width="961"></page>
</pages>
Here my select
<xsl:for-each select="/prototype/pages/page[@width != (preceding-sibling::*/@width) or @height != (preceding-sibling::*/@height) or position() = 1]">
<div class="{@width}x{@height} {@id}"></div>
</xsl:for-each>
Here the result:
<div class="960x1000 page0001"></div>
<div class="961x1000 page6342"></div>
<div class="961x1001 page7995"></div>
<div class="960x1001 page4461"></div>
<div class="960x1001 page4690"></div>
<div class="961x1001 page9001"></div>
What I expect to have
<div class="960x1000 page0001"></div>
<div class="961x1000 page6342"></div>
<div class="961x1001 page7995"></div>
<div class="960x1001 page4461"></div>
I know that my select is incorrect and I understand why. Is there any possibility to modify it in order to get the right result?
Upvotes: 0
Views: 907
Reputation: 122424
The issue is that comparison operators such as =
and !=
in XPath are implicitly existentially quantified, in other words
@width != preceding-sibling::*/@width
checks whether there is any preceding sibling whose width is different from mine. What you actually want in this case is that the combination of width and height for this node is different from the combination of width and height for any of its predecessors. You could do this with a for-each
over all pages and a test within the body of the for-each
<xsl:for-each select="/prototype/pages/page">
<xsl:variable name="myWidth" select="@width" />
<xsl:variable name="myHeight" select="@height" />
<xsl:if test="not(preceding-sibling::*[@height=$myHeight][@width=$myWidth])">
<div class="{@width}x{@height} {@id}"></div>
</xsl:if>
</xsl:for-each>
Alternatively you can treat it as a grouping problem - group the page
elements by their width and height, and output one div per group. In XSLT 2.0 this could be
<xsl:for-each-group select="/prototype/pages/page"
group-by="concat(@width, 'x', @height)">
<div class="{current-grouping-key()} {@id}" />
</xsl:for-each-group>
In 1.0 you can get the same effect using the Muenchian grouping method (there are many other questions you can find with the search function that explain it well so I won't repeat it again here).
Upvotes: 1