Given the following xml:
<container>
<val>2</val>
<id>1</id>
</container>
<container>
<val>2</val>
<id>2</id>
</container>
<container>
<val>2</val>
<id>3</id>
</container>
<container>
<val>4</val>
<id>1</id>
</container>
<container>
<val>4</val>
<id>2</id>
</container>
<container>
<val>4</val>
<id>3</id>
</container>
I’d like to return something like
2 - 1
2 - 3
4 - 1
4 - 3
Using a nodeset I’ve been able to get the last occurrence via:
exsl:node-set($list)/container[not(val = following::val)]
but I can’t figure out how to get the first one.
To get the first and the last occurrence (document order) in each “
<val>” group, you can use an<xsl:key>like this:EDIT #1: A bit of an explanation since this might not be obvious right away:
<xsl:key>returns all<container>nodes having a given<val>. You use thekey()function to query it.<xsl:variable>is where it all happens. It reads as:<container>nodes in the document (“//container“) check…generate-id()) as the first node returned bykey()or the last node returned bykey()key('ContainerGroupByVal', val)returns the set of<container>nodes matching the current<val><xsl:for-each>does the output. It could just as well be a<xsl:apply-templates>.EDIT #2: As Dimitre Novatchev rightfully points out in the comments, you should be wary of using the “
//” XPath shorthand. If you can avoid it, by all means, do so — partly because it potentially selects nodes you don’t want, and mainly because it is slower than a more specific XPath expression. For example, if your document looks like:then you should use “
/containers/container” or “/*/container” instead of “//container“.EDIT #3: An alternative syntax of the above would be:
Explanation: The XPath union operator “
|” combines it’s arguments into a node-set. By definition, a node-set cannot contain duplicate nodes — for example: “. | . | .” will create a node-set containing exactly one node (the current node).This means, if we create a union node-set from the current node (“.”), the “
key(…)[1]” node and the “key(…)[last()]” node, it’s node count will be 2 if (and only if) the current node equals one of the two other nodes, in all other cases the count will be 3.