Search code examples
xmlxslt-2.0xpath-2.0

XPath: Compare first & last of all attributes & return unique values


I wish to compared all the first & last attributes with each other & return only the unique values.

XML:

  <way>
    <nd ref="1"/>
    <nd ref="2"/>
    <nd ref="3"/>
    <tag k="highway"/>
  </way>
  <way>
    <nd ref="2"/>
    <nd ref="3"/>
    <nd ref="4"/>
    <tag k="highway"/>
  </way>
  <way>
    <nd ref="4"/>
    <nd ref="6"/>
    <nd ref="1"/>
    <tag k="highway"/>
  </way>
  <way>
    <nd ref="2"/>
    <nd ref="4"/>
    <nd ref="7"/>
    <tag k="highway"/>
  </way>
</osm>

Expected output:

<nd ref="3"/>
<nd ref="7"/>

I don't have much in the way of XSL. I've been playing around with the two value-of & not getting very far:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> 
<xsl:variable name="LastRef" select="osm/way/nd"/>
<xsl:template match="/">
    <!-- <xsl:value-of select="osm/way/nd[1]/@ref = osm/way/nd[last()]/@ref"/>  -->
    <xsl:value-of select="osm/way/nd[position() = 1 or position() = last()][not($LastRef)]"/> 
</xsl:template>
</xsl:stylesheet>

I've also been looking at preceding:: based on this page: https://www.oreilly.com/library/view/xslt-cookbook/0596003722/ch04s03.html

But I'm scrabbling around in the dark. Hope you can help


Solution

  • I think it sounds as if you want to group those nd elements (first and last) by the ref attribute and only output a group if it has a single member, that can be expressed in XSLT 2 or 3 as

      <xsl:template match="osm">
        <xsl:for-each-group select="way/nd[position() = (1, last())]" group-by="@ref">
          <xsl:sequence select=".[not(current-group()[2])]"/>
        </xsl:for-each-group>
      </xsl:template>