xsl-list
[Top] [All Lists]

Re: Concatenating a nodeset (set of attributes)

2005-10-09 17:02:38
Hi Ragulf,

I realized after sending my answer that it contained various flaws. You will 
have to divide by
larger numbers (powers of 100 or 1000) to cope with more child STRUC elements. 
But dividing by
larger numbers cannot be done endlessly. I think the precision of real numbers 
usually ends around 1
divided by 2 billion. So you either can handle more depth or more children, not 
both to a reasonable
extend. Next to this, number() and format-number() of an empty node-set returns 
'NaN'. So, sorting
on numbers might look smart, but is actually a lot more complicated...

But your format-number brings me to the following idea. Why not use it to 
construct a string that
can be used to sort alphanumerically:

      <xsl:sort select="concat(format-number(@pos, '000'),
                          translate(format-number(STRUC/@pos, '000'), 'Na', 
'00'),
                          translate(format-number(STRUC/STRUC/@pos, '000'), 'Na', 
'00'))"
                data-type="text" />

The 'NaN's are less problematic here. Just convert the characters to zeros and 
alphanumerical
ordering will not be disturbed. This however still limits the nesting level to 
the amount of levels
you anticipated. If you want to do better (in XSLT 1.0?), you will have to do 
two steps.

In the first step you add the string that is constructed with the select 
expression of the sort as
an attribute to the top-most STRUC element, extending the sort-key as long as 
the nesting continues,
 and using a format-string with more zeros if more child elements have to be 
supported.

      <xsl:attribute name="sort-key">
        <xsl:value-of select="format-number(@pos, '000')" />
        <!-- recurse over nested levels -->
        <xsl:apply-templates select="STRUC" mode="add-sort-key" />
      </xsl:attribute>

  ...

  <xsl:template match="STRUC/STRUC" mode="add-sort-key">
    <xsl:value-of select="format-number(@pos, '000')" />
    <xsl:apply-templates select="STRUC" mode="add-sort-key" />
  </xsl:template>

In the second step you simply sort on that attribute:

          <xsl:apply-templates select="STRUC" mode="remove-sort-key">
            <xsl:sort select="@sort-key" data-type="number" />
          </xsl:apply-templates>

(removing the attribute again if it disturbs further processing)

You could use the node-set() extension function that is supported by most 
parsers to perform the two
passes in one stylesheet, if you like. Simply gather the result of the first 
pass in a variable and
pass the variable as an argument to the node-set() function and pass the result 
to another
apply-templates:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
  xmlns:exsl="http://exslt.org/common";>

  ...

      <xsl:variable name="rtf">
        <xsl:apply-templates select="STRUC" mode="add-sort-key" />
      </xsl:variable>

      <xsl:apply-templates select="exsl:node-set($rtf)/STRUC" 
mode="remove-sort-key">
        <xsl:sort select="@sort-key" data-type="number" />
      </xsl:apply-templates>

Note, I'm using the exslt namespace for lesser dependence on the actual parser.

Just a last remark. It would be much easier to solve this problem if the STRUC 
elements were nested
as a tree. Something like:

<TOC>
  <STRUC pos="2">
    <STRUC pos="1" />
    <STRUC pos="12" />
  </STRUC>
  <STRUC pos="3">
    <STRUC pos="4" />
    <STRUC pos="4">
      <STRUC pos="1" />
    </STRUC>
  </STRUC>
</TOC>

HTH,
Geert



--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--