Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Distinct elements and grouping

Tags:

xml

xslt

xpath

Given the following xml fragment:

<Problems>
  <Problem>
    <File>file1</File>
    <Description>desc1</Description>
  </Problem>
  <Problem>
    <File>file1</File>
    <Description>desc2</Description>
  </Problem>
  <Problem>
    <File>file2</File>
    <Description>desc1</Description>
  </Problem>
</Problems>

I need to produce something like

<html>
  <body>
    <h1>file1</h1>
    <p>des1</p>
    <p>desc2</p>
    <h1>file2</h1>
    <p>des1</p>
  </body>
</html>

I tried using a key, like

<xsl:key name="files" match="Problem" use="File"/>

but I don't really understand how to take it to the next step, or if that's even the right approach.

like image 839
rjohnston Avatar asked Dec 30 '08 00:12

rjohnston


People also ask

What does distinct elements mean?

The objects in some collection are described as "distinct" if each is different from the others. So an array with distinct elements is one where no two elements are the same.

What are distinct elements in math?

Answer: In math, the term distinct number is used to refer to a number in a set that is not equal to another number. For example, the set of numbers {1, 2} contains the two distinct numbers 1 and 2, which can be proven by evaluating different traits of each number.

What is the number of distinct elements in a set called?

Cardinality of sets The number of elements in a particular set is a property known as cardinality; informally, this is the size of a set. In the above examples, the cardinality of the set A is 4, while the cardinality of set B and set C are both 3.

How do you know if a group is finite?

If G is a finite group, every g ∈ G has finite order. The proof is as follows. Since the set of powers {ga : a ∈ Z} is a subset of G and the exponents a run over all integers, an infinite set, there must be a repetition: ga = gb for some a<b in Z. Then gb−a = e, so g has finite order.


1 Answers

This solution is a little bit simpler, more efficient and at the same time more general than the one presented by Richard:

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!--                                            -->
 <xsl:key name="kFileByVal" match="File"
       use="." />
<!--                                            -->
 <xsl:key name="kDescByFile" match="Description"
       use="../File"/>
<!--                                            -->
    <xsl:template match="/*">
     <html>
      <body>
      <xsl:for-each select=
         "*/File[generate-id()
                =
                 generate-id(key('kFileByVal',.)[1])]">
        <h1><xsl:value-of select="."/></h1>
        <xsl:for-each select="key('kDescByFile', .)">
          <p><xsl:value-of select="."/></p>
        </xsl:for-each>
      </xsl:for-each>
      </body>
     </html>
    </xsl:template>
</xsl:stylesheet>

when applied to the provided XML document:

<Problems>
    <Problem>
        <File>file1</File>
        <Description>desc1</Description>
    </Problem>
    <Problem>
        <File>file1</File>
        <Description>desc2</Description>
    </Problem>
    <Problem>
        <File>file2</File>
        <Description>desc1</Description>
    </Problem>
</Problems>

Produces the wanted result:

<html>
   <body>
      <h1>file1</h1>
      <p>desc1</p>
      <p>desc2</p>
      <h1>file2</h1>
      <p>desc1</p>
   </body>
</html>

Do note the simple match pattern of the first <xsl:key> and how, using a second <xsl:key>, we locate all "Description" elements that are siblings of a "File" element that has a given value.

We could have used more templates instead of <xsl:for-each> pull-processing, however this is a quite simple case and the solution really benefits from shorter, more compact and more readable code.

Also note, that in XSLT 2.0 one will typically use the <xsl:for-each-group> instruction instead of the Muenchian method.

like image 124
Dimitre Novatchev Avatar answered Nov 10 '22 21:11

Dimitre Novatchev