views:

56

answers:

3

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"&gt;

  <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?

+1  A: 

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"&gt;
 <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
Dimitre Novatchev
We've got to stop meeting like this...
Daniel Martin
@Daniel-Martin: ??????
Dimitre Novatchev
Oh, it's just once again: question from the same guy about xsl, same two people answer, once again your answer comes in while I'm typing mine, and the answers each of us gave to this question are very similar to what each of us gave to his earlier question.
Daniel Martin
hehe hey its good to have both approaches :) i will be generation the position depending on the number of root elements and i cant create key on the run so im using daniel's method .. still thank you very enlightening
Nathan Colin
@Daniel-Martin: Why, this is fun. Hope to meet you like this on SO many more times in the future! :) Glad we have similar interests.
Dimitre Novatchev
@Daniel-Martin: Or you can drop me a line. I'm dnovatchev on 10^100's mail.
Dimitre Novatchev
+1  A: 

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))))">
Daniel Martin
Reasonable. I hate making keys "too compound", so I'm using two simpler keys.
Dimitre Novatchev
Awesome thanks !!
Nathan Colin
A: 

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
DevNull
i do not mind its good to have the reference :)
Nathan Colin