[xsl] Finding triples with same/distinct attribute value

Subject: [xsl] Finding triples with same/distinct attribute value
From: Michael Müller-Hillebrand <mmh@xxxxxxxxxxxxx>
Date: Wed, 29 Feb 2012 21:26:48 +0100
Dear gurus,

I have a solution but I am eager to learn whether there is a better method to
solve the problem.

There is a number of elements with an attribute. The task is to find every
triple of elements with the same attribute value or with distinct attribute
values. I solved it using xsl:for-each loops but feel like having missed some
XPath 2 features. I am looking forward to your comments.

- Michael

PS: I get a result of 60 triples with the sample file.

Input:

<pots>
 <pot a="3" />
 <pot a="3" />
 <pot a="3" />
 <pot a="1" />
 <pot a="1" />
 <pot a="1" />
 <pot a="1" />
 <pot a="1" />
 <pot a="1" />
 <pot a="3" />
 <pot a="2" />
 <pot a="3" />
</pots>

XSL:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; version="2.0"
 xmlns:xs="http://www.w3.org/2001/XMLSchema";
 xmlns:my="http://my";
 exclude-result-prefixes="xs my"
 >
 <xsl:output indent="yes"/>

 <xsl:template match="pots">
  <result>
   <xsl:sequence select="my:triples(*)" />
  </result>
 </xsl:template>

 <xsl:function name="my:triples" as="node()*">
  <xsl:param name="nodes" as="node()+"/>

  <!-- common values -->
  <xsl:for-each select="$nodes">
   <xsl:variable name="t1" select="."/>
   <xsl:for-each select="$t1/following-sibling::*[@a eq $t1/@a]">
    <xsl:variable name="t2" select="."/>
    <xsl:for-each select="$t2/following-sibling::*[@a eq $t1/@a]">
     <triple>
      <xsl:value-of select="string-join((my:pos($nodes, $t1), my:pos($nodes,
$t2), my:pos($nodes, .)), ' ')" />
     </triple>
    </xsl:for-each>
   </xsl:for-each>
  </xsl:for-each>

  <!-- distinct values -->
  <xsl:for-each select="$nodes">
   <xsl:variable name="t1" select="."/>
   <xsl:for-each select="$t1/following-sibling::*[@a ne $t1/@a]">
    <xsl:variable name="t2" select="."/>
    <xsl:for-each select="$t2/following-sibling::*[@a ne $t1/@a and @a ne
$t2/@a]">
     <triple>
      <xsl:value-of select="string-join((my:pos($nodes, $t1), my:pos($nodes,
$t2), my:pos($nodes, .)), ' ')" />
     </triple>
    </xsl:for-each>
   </xsl:for-each>
  </xsl:for-each>

 </xsl:function>

 <xsl:function name="my:pos" as="xs:string">
  <xsl:param name="nodes" as="node()+"/>
  <xsl:param name="node" as="node()"/>
  <xsl:value-of select="count($nodes[$node >> .]) + 1" />
 </xsl:function>

</xsl:transform>

Current Thread