Nathan Colin
Nathan Colin

Reputation: 13

XSL unique values per node per position

this get ever more complicated :)

now i face another issue in last question we managed to take unique values from only one parent node

now with:

<?xml version="1.0" encoding="ISO-8859-1"?>
<roots>
     <root>
          <name>first</name>
          <item>
               <something>A</something>
               <something>A</something>
          </item>
          <item>
               <something>B</something>
               <something>A</something>
          </item>
          <item>
               <something>C</something>
               <something>P</something>
          </item>
          <item>
               <something>A</something>
               <something>L</something>
          </item>
          <item>
               <something>A</something>
               <something>A</something>
          </item>
          <item>
               <something>B</something>
               <something>A</something>
          </item>
          <item>
               <something>D</something>
               <something>A</something>
          </item>
     </root>
     <root>
          <name>second</name>
          <item>
               <something>E</something>
               <something>A</something>
          </item>
          <item>
               <something>B</something>
               <something>A</something>
          </item>
          <item>
               <something>F</something>
               <something>A</something>
          </item>
          <item>
               <something>A</something>
               <something>A</something>
          </item>
          <item>
               <something>A</something>
               <something>A</something>
          </item>
          <item>
               <something>B</something>
               <something>H</something>
          </item>
          <item>
               <something>D</something>
               <something>G</something>
          </item>
     </root>
</roots>

now i need to get the unique values depending only from one node before but just from the elements on the second position

  <?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output indent="yes" method="text"/>
  <xsl:key name="item-by-value" match="something"
 use="concat(normalize-space(.), ' ', generate-id(./ancestor::root))"/>
<xsl:key name="rootkey" match="root" use="name"/>
  <xsl:template match="/">
 <xsl:for-each select="key('rootkey','first')">
<xsl:for-each select="item/something[1]">
<xsl:sort />
  <xsl:if test="generate-id() = generate-id(key('item-by-value', 
                  concat(normalize-space(.), ' ', generate-id(./ancestor::root))))">
  <xsl:value-of select="."/>
 </xsl:if>
</xsl:for-each> 
<xsl:text>_________</xsl:text>
<xsl:for-each select="item/something[2]">
<xsl:sort />
  <xsl:if test="generate-id() = generate-id(key('item-by-value', 
                  concat(normalize-space(.), ' ', generate-id(./ancestor::root))))">
  <xsl:value-of select="."/>
 </xsl:if>
</xsl:for-each> 
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

with this XSL i get ABCD_________LP where the result i need is ABCD_________ALP

any ideas?

Upvotes: 1

Views: 924

Answers (3)

Daniel Haley
Daniel Haley

Reputation: 52888

I know you're restricted to XSLT 1.0 and you're using Xalan, but I'm going to add this answer just in case it might help someone else doing a search in the future. (That can use XSLT 2.0.) Hope you don't mind.

Also, I'm using Saxon-HE 9.2.0.6 for the processor.

Here's the stylesheet:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
   <xsl:output method="text"/>

   <xsl:template match="/roots">
      <xsl:for-each select="root">
         <xsl:value-of select="distinct-values(item/something[1])"/>___<xsl:value-of select="distinct-values(item/something[2])"/>
         <xsl:text>&#xA;</xsl:text>
      </xsl:for-each>
   </xsl:template>

</xsl:stylesheet>

Here's the output using your XML:

A B C D___A P L
E B F A D___A H G

If you wanted to strip the spaces from the output, you could put the output in a variable and then use replace to strip the spaces. (Anyone know of a better way?)

Stylesheet:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
   <xsl:output method="text"/>

   <xsl:template match="/roots">
      <xsl:variable name="output">
         <xsl:for-each select="root">
            <xsl:value-of select="distinct-values(item/something[1])"/>___<xsl:value-of select="distinct-values(item/something[2])"/>
            <xsl:text>&#xA;</xsl:text>
         </xsl:for-each>
      </xsl:variable>
      <xsl:value-of select="replace($output,' ','')"/>
   </xsl:template>

</xsl:stylesheet>

Output:

ABCD___APL
EBFAD___AHG

Upvotes: 0

Daniel Martin
Daniel Martin

Reputation: 23578

Once again, the issue is that if you want to say "the first node with this content under this root appearing in this position in the item node", then you have to add "position in the item node" to the key. You can either do this by having two separate keys, as Dimitre's solution does, or change your key to:

use="concat(normalize-space(.), ' ', 
     count(./preceding-sibling::something), ' ', generate-id(./ancestor::root))"/>

And then make your two test expressions look like:

<xsl:if test="generate-id() = generate-id(key('item-by-value', 
              concat(normalize-space(.), ' 0 ', generate-id(./ancestor::root))))">

and:

<xsl:if test="generate-id() = generate-id(key('item-by-value', 
              concat(normalize-space(.), ' 1 ', generate-id(./ancestor::root))))">

Upvotes: 1

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243599

Just a slight modification to my answer to your previous question and you've got it!

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:key name="kSomethingByNameAndVal-1" match="something[1]"
  use="concat(../../name, '+', .)"/>

 <xsl:key name="kSomethingByNameAndVal-2" match="something[2]"
  use="concat(../../name, '+', .)"/>

 <xsl:template match="/">
   <xsl:for-each select="*/*">
     <xsl:for-each select=
      "item/something[1]
             [generate-id()
             =
              generate-id(key('kSomethingByNameAndVal-1',
                               concat(../../name, '+', .)
                              )
                          )
             ]
      ">

       <xsl:value-of select="."/>
     </xsl:for-each>
     <xsl:text>&#xA;</xsl:text>

     <xsl:for-each select=
      "item/something[2]
             [generate-id()
             =
              generate-id(key('kSomethingByNameAndVal-2',
                               concat(../../name, '+', .)
                              )
                          )
             ]
      ">

       <xsl:value-of select="."/>
     </xsl:for-each>
     <xsl:text>&#xA;</xsl:text>
   </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document, The wanted, correct results are produced:

ABCD
APL
EBFAD
AHG

Upvotes: 1

Related Questions