Question

I have an XML format as follows:

<Items>
  <Item id="1">
  </Item>
  <Item id="2">
  </Item>
  <Item id="1">
  </Item>
  <Item id="3">
  </Item>
</Items>

I am trying to declare an XSLT 2.0 variable that will contain all elements where "id" is unique. If the "id" is NOT unique, only the first instance should be output, such that the variable will store:

<Item id="1">
</Item>
<Item id="2">
</Item>
<Item id="3">
</Item>

The following code will give me all of the unique @id attributes, but NOT their parent (Item):

<xsl:variable name="uniqueItems" select="distinct-values(/Items/Item/@id)"/>

I want to do somelike this:

<xsl:variable name="uniqueItems" select="distinct-values(/Items/Item/@id)/parent::node()"/>

But this is obviously not correct.

Was it helpful?

Solution

Given

<xsl:key name="id" match="Item" use="@id"/>

you could use <xsl:variable name="uniqueItems" select="/Items/Item[not(key('id', @id)[2])]"/>.

Or use for-each-group to find groups of single items:

<xsl:variable name="uniqueItems" as="element(Item)">
  <xsl:for-each-group select="/Items/Item" group-by="@id">
   <xsl:if test="not(current-group()[2])">
    <xsl:sequence select="."/>
   </xsl:if>
  </xsl:for-each-group>
</xsl:variable>

If you want to eliminate duplicates then with

<xsl:variable name="uniqueItems" as="element(Item)">
  <xsl:for-each-group select="/Items/Item" group-by="@id">
   <xsl:sequence select="."/>
  </xsl:for-each-group>
</xsl:variable>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top