Re: [xsl] Problem to place data into a specific <TD>

Subject: Re: [xsl] Problem to place data into a specific <TD>
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Thu, 21 Dec 2000 11:43:38 +0000
Hi Roel,

> I want this as output (with xsl)
>
>         A    B    C
> 1      1     2    3
> 2      1           3
>
> with a for-each the problem is the last code is placed under B like:
>
>         A    B    C
> 1      1     2    3
> 2      1     3

This is kind of a grouping problem.  You need to know what the
possible code types are within your document as a whole and then, for
each of the rows, cycle through those code types, giving the value for
that code type if there is one, and nothing if there isn't.

You can get the unique code types by getting all the codes that are
first of their type, and then looking at their types.  To select all
the codes, use:

  /Root/code

To filter those codes to select only those that are first of their
type, you can check whether there are any preceding code elements that
have the same type: if there are, then that particular type has
already been counted:

  /Root/code[not(preceding-sibling::code/@type = @type)]

This method of filtering can be quite inefficient if you've got lots
of codes. An alternative is to use the Muenchian method to get a list
of the code types. First, define a key to index all the 'code'
elements according to their 'type' attribute:

<xsl:key name="code-types"
         match="code"
         use="@type" />

This means that you can retrieve all the 'code' elements with a
specific type using the key() function.  For example,
key('code-types', 'A') will give you all the codes with a type
attribute equal to 'A'.  So you can find out whether the particular
code is the first with a particular type by seeing if it's the first
in the list retrieved by the key for that type.  You can do this with:

  /Root/code[generate-id() = generate-id(key('code-types', @type)[1])]

[i.e. select those codes whose unique ID is the same as the unique ID
of the first code with the same type retrieved from the code-types
key] or:

  /Root/code[count(. | key('code-types', @type)[1]) = 1]

[i.e. select those codes such that a union of the code with the first
code with the same type retrieved from the code-types key gives a node
list that is only one node long].

So, you can set up a list of the unique code types within your
document in a variable:

<xsl:variable name="code-types"
              select="/Root/code[generate-id() =
                                 generate-id(key('code-types',
                                                 @type)[1])]/@type" />

Do this at the top level, or at least not within any of the
xsl:for-eaches: you only want and need to do it once.

Now, when you go through each set of codes to create a row, you can
use the $code-types variable to drive the creation of the cells.
Within the xsl:for-each that's creating each row, have a xsl:for-each
that iterates over the $code-types variable:

  <xsl:for-each select="$code-types">
    ...
  </xsl:for-each>

Let's say that the code elements for a particular row are in a
variable called $codes.  For each of the code types, you want to pick
out the code that has the same type as the type you're currently
looking at.  If there is such a code, you want to give its value; if
there isn't, then you want an empty cell in the table.

Within the xsl:for-each on the $code-types, the current node is a
'type' attribute.  To get the codes within the $codes variable with a
matching type, you use:

  $codes[@type = current()]

So, to give that value within the cell, use:

  <xsl:for-each select="$code-types">
    <td>
      <xsl:value-of select="$codes[@type = current()]" />
    </td>
  </xsl:for-each>

By the way, if you're not already, you might find it helpful to use
keys to group the codes by ID, something like:

<xsl:key name="codes-by-ID"
         match="code"
         use="preceding-sibling::ID[1]" />

and then:

  <xsl:for-each select="ID">
    <xsl:variable name="codes" select="key('codes-by-ID', .)" />
    <tr>
      <xsl:for-each select="$code-types">
        <td>
          <xsl:value-of select="$codes[@type = current()]" />
        </td>
      </xsl:for-each>
    </tr>
  </xsl:for-each>
  
I hope that helps,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/



 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread