Reputation: 19
Using XPath 1, I'm trying to return a node set of item
s within a certain range which have distinct dept_number
values within them. The range which these items are in is dynamic. The document is structured like the following:
<data>
<items>
<item>
<dept_number>1</dept_number>
</item>
<item>
<dept_number>1</dept_number>
</item>
<item>
<dept_number>2</dept_number>
</item>
<item>
<dept_number>2</dept_number>
</item>
<item>
<dept_number>3</dept_number>
</item>
<item>
<dept_number>4</dept_number>
</item>
<item>
<dept_number>4</dept_number>
</item>
</items>
</data>
The best way I could think of doing this is like the following:
/data/items/item[not(dept_number = preceding-sibling::item[position() > $min]/dept_number) and position() > $min and position() <= $max]/dept_number
where $min
is the lower bound of the range and $max
is the upper bound. This is not working. For instance, if I use
/data/items/*[name() = 'item' and not(dept_number = preceding-sibling::*[position() > 1]/dept_number) and position() > 1 and position() <= 5]/dept_number
it returns
<dept_number>1</dept_number>
<dept_number>2</dept_number>
<dept_number>2</dept_number>
<dept_number>3</dept_number>
which is not a set of distinct values. What I would expect is
<dept_number>1</dept_number>
<dept_number>2</dept_number>
<dept_number>3</dept_number>
I believe the issue is with using position()
within a nested context (i.e. /data/items/item[... preceding-sibling::item[position() > $min] ...]
. It seems to be using the result you would get from position()
when used in the outer context, not the inner one.
Within xslt, I have also tried to use keys as such:
<xsl:key name="dept_number_key" match="/data/items/item" use="dept_number"/>
<xsl:for-each select="/data/items/item[generate-id() = generate-id(key('dept_number_key', dept_number)[1]) and position() $gt; $min-pos and position() <= $max-pos]">
</xsl:for-each>
but this still does not work.
Any help would be much appreciated.
Edit: The range I am referring to is not a range of dept_number values, but a range of <item>
tags within <items>
. For instance, if the range was 3 to 5 I would want to get back the third, fourth and fifth <item>
tag within <items>
.
Upvotes: 0
Views: 277
Reputation: 116959
--- edited in response to clarification ---
I would start by copying the items in range to a new "document", so that you can later use Muenchian grouping on them to get only distinct values. Without this, the grouping can fail if, for example, item #3 is not the first item with the given dept_number
value.
Consider the following stylesheet:
XSLT 1.0 + EXSLT node-set() function
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="min-pos" select="3" />
<xsl:variable name="max-pos" select="5" />
<xsl:key name="dept_number_key" match="item" use="dept_number" />
<xsl:template match="/data">
<xsl:variable name="items-in-range">
<xsl:copy-of select="items/item[$min-pos <= position() and position() <= $max-pos]"/>
</xsl:variable>
<root>
<xsl:copy-of select="exsl:node-set($items-in-range)/item[generate-id() = generate-id(key('dept_number_key', dept_number)[1])]/dept_number"/>
</root>
</xsl:template>
</xsl:stylesheet>
Note that some XSLT 1.0 processors support the EXSLT set:distinct()
extension function. This could shorten the code here.
Upvotes: 1