Re: problem sorting uniquely w/transform()

Subject: Re: problem sorting uniquely w/transform()
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Sun, 10 Sep 2000 19:01:33 +0100
Shane,

>i am having a problem sorting uniquely on an element name...  i'm
>translating the <LEAGUE NAME="..."> element to uppercase and seeing if it
>matches any preceding elements.  

In the comparison that does not involve case-insensitive translation, you
select:

  //LEAGUE[not(@NAME = preceding::*/@NAME)]

Within equality tests, the way the result is worked out depends on the type
of the nodes that are involved.  When they involve node sets (as in this
case), the equality expression returns true if there are nodes within the
node set(s) for which the equality expression will be true.  In other
words, "@NAME = preceding::*/@NAME" returns true if *any* of the preceding
elements has a NAME attribute that matches the NAME attribute of the
current node.

When you select with the translation:

  //LEAGUE[not(translate(@NAME,$lower,$upper) = 
               translate(preceding::*/@NAME,$lower,$upper))]

things work differently because the translate() function returns a string.
Doing:

  translate(preceding::*/@NAME, $lower, $upper)

translates the string value of the node set preceding::*/@NAME from lower
case to upper case, and returns this string.  The string value of a node
set is the string value of the first node in the node set - the value of
the first preceding element's NAME attribute.  That means that you're
testing the equality of the translated @NAME of the current element with
the translated @NAME of the first preceding element, not comparing it with
all the other preceding element's @NAMEs.

I don't *think* it's possible to do the selection you're after with a
single select expression, but you could get around it by doing a
xsl:for-each on all the //LEAGUE elements, and containing within it an
xsl:if that only retrieved those who don't have a preceding element with
the same (translated) name:

<xsl:for-each select="//LEAGUE">
  <xsl:sort select="@NAME" />
  <xsl:variable name="name" select="translate(@NAME, $lower, $upper)" />
  <xsl:if test="not(preceding::*[translate(@NAME, $lower, $upper) = $name])">
    <!-- do stuff -->
  </xsl:if>
</xsl:for-each>

If possible, for efficiency, you should probably give a more exact
indication of the LEAGUE elements you're interested in (e.g.
'/SCORES/LEAGUE') and if you're only interested in the
preceding-sibling::LEAGUE elements, you should use this rather than the
general preceding::*.  Generally, more specific XPath expressions are more
efficient.

I hope that this helps,

Jeni

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


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


Current Thread