Re: [xsl] Finding triples with same/distinct attribute value

Subject: Re: [xsl] Finding triples with same/distinct attribute value
From: Michael Kay <mike@xxxxxxxxxxxx>
Date: Wed, 29 Feb 2012 21:27:11 +0000
On 29/02/2012 20:26, Michael M|ller-Hillebrand wrote:
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.
I think you need to state the requirement more clearly. Reverse-engineering it from your code isn't that easy. Is your definition of a "triple" this: "a sequence of three distinct <pot> elements, not necessarily adjacent, but in document order"?

Michael Kay
Saxonica

- 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