Problem with XSL and sorting

C

Christoph

I'm trying to come up with a stylesheet where, when the rows are displayed,
duplicate game names are not shown on subsequent rows. It works but doesn't
work properly. If I sort the data using <xsl:sort> prior to processing,
it's not checking against the previous row after the sort but instead the
previous row from the original data set. Here is the XML and XSL I'm using:

<GameSets>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>10th Anniversary</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Anarchs</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Ancient Hearts</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Bloodlines</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Camarilla</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Netrunner</gamename>
<setname>Classic</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Dark Sovereigns</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Final Nights</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Gehenna</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Jyhad</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Kindred Most Wanted</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Legacies of Blood</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Netrunner</gamename>
<setname>Netrunner</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Nights of Reckoning</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Promotional</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Netrunner</gamename>
<setname>Proteus</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Sabbat</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Sabbat War</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>The Black Hand</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>V:TES</setname>
</GameSetsRow>
</GameSets>

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:eek:utput method="html"/>
<xsl:template match="GameSets">
<html>
<head>
<title>Game Sets</title>
</head>
<body>
<h1>Game Sets</h1>
<table border="1">
<tr>
<th>Position</th>
<th>Game</th>
<th>Set</th>
<th>Previous Game</th>
</tr>
<xsl:for-each select="GameSetsRow">
<xsl:sort select="gamename"/>
<xsl:variable name="pos" select="position()"/>
<tr>
<td><xsl:copy-of select="$pos"/></td>
<td>
<xsl:if test="$pos = 1 or gamename !=
preceding-sibling::GameSetsRow[1]/gamename">
<xsl:copy-of select="gamename"/>
</xsl:if>
</td>
<td><xsl:copy-of select="setname"/></td>
<td><xsl:copy-of
select="preceding-sibling::GameSetsRow[1]/gamename"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

What am I missing? Why isn't the test using the sorted set?

thnx,
Christoph
 
D

Dimitre Novatchev

What am I missing? Why isn't the test using the sorted set?

The result of <xsl:sort/> is that the current node is selected from the
node-list (of xsl:for-each or xsl-apply-templates) in sorted order.

This does not change the xml documents and any of the axis relationships
between its nodes -- the preceding sibling of any node still remains the
same.

What you need is to create a completely new document, in which the sorted
nodes will indeed be siblings.

This can be done if the sorting is performed in the body of an xsl:variable.
The resulting variable is of type RTF (Result Tree Fragment) and cannot
directly be operated by XPath (except for treating it as a string). One has
to convert this RTF to a regular tree by applying the xxx:node-set()
extension function on it. The xxx:node-set() ext. fn. is usually provided by
the particular XSLT Processor vendor and the prefix "xxx" is typically bound
to a vendor-specific namespace. If the XSLT Processor supports EXSLT, then
the exslt:node-set() ext. fn. can be used and the xslt code will be portable
accross XSLT processors that support EXSLT.

In XSLT finding unique nodes is also known as "grouping". More on grouping
can be found at:

http://www.jenitennison.com/xslt/grouping/


Cheers,
Dimitre Novatchev

Christoph said:
I'm trying to come up with a stylesheet where, when the rows are
displayed, duplicate game names are not shown on subsequent rows. It
works but doesn't work properly. If I sort the data using <xsl:sort>
prior to processing, it's not checking against the previous row after the
sort but instead the previous row from the original data set. Here is the
XML and XSL I'm using:

<GameSets>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>10th Anniversary</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Anarchs</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Ancient Hearts</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Bloodlines</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Camarilla</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Netrunner</gamename>
<setname>Classic</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Dark Sovereigns</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Final Nights</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Gehenna</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Jyhad</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Kindred Most Wanted</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Legacies of Blood</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Netrunner</gamename>
<setname>Netrunner</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Nights of Reckoning</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Promotional</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Netrunner</gamename>
<setname>Proteus</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Sabbat</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Sabbat War</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>The Black Hand</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>V:TES</setname>
</GameSetsRow>
</GameSets>

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:eek:utput method="html"/>
<xsl:template match="GameSets">
<html>
<head>
<title>Game Sets</title>
</head>
<body>
<h1>Game Sets</h1>
<table border="1">
<tr>
<th>Position</th>
<th>Game</th>
<th>Set</th>
<th>Previous Game</th>
</tr>
<xsl:for-each select="GameSetsRow">
<xsl:sort select="gamename"/>
<xsl:variable name="pos" select="position()"/>
<tr>
<td><xsl:copy-of select="$pos"/></td>
<td>
<xsl:if test="$pos = 1 or gamename !=
preceding-sibling::GameSetsRow[1]/gamename">
<xsl:copy-of select="gamename"/>
</xsl:if>
</td>
<td><xsl:copy-of select="setname"/></td>
<td><xsl:copy-of
select="preceding-sibling::GameSetsRow[1]/gamename"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

What am I missing? Why isn't the test using the sorted set?

thnx,
Christoph
 
C

Christoph

extension function on it. The xxx:node-set() ext. fn. is usually provided
by the particular XSLT Processor vendor and the prefix "xxx" is typically
bound to a vendor-specific namespace. If the XSLT Processor supports
EXSLT, then the exslt:node-set() ext. fn. can be used and the xslt code
will be portable accross XSLT processors that support EXSLT.

Is there a XSLT processor that works in both IE and Firefox?
In XSLT finding unique nodes is also known as "grouping". More on grouping
can be found at:
http://www.jenitennison.com/xslt/grouping/

I've read alot about the Muenchian Method alot today while looking into how
I
might be able to solve this problem. The main thing I've been running into
is that
it doesn't seem to work properly with a <xsl:sort> sorted data set...
Nothing I've
tried using this method seemed to do the trick. :(

thnx,
Christoph
 
J

Joe Kesselman

Christoph said:
Is there a XSLT processor that works in both IE and Firefox?

Well, you could write an applet that downloads and runs Xalan... but
that's not a particularly efficient solution. If you aren't willing to
trust the XSLT processor provided by the browser (or that the browser
provides one at all), you may want to do this processing in the server.
 
D

Dimitre Novatchev

Christoph said:
Is there a XSLT processor that works in both IE and Firefox?


I've read alot about the Muenchian Method alot today while looking into
how I
might be able to solve this problem. The main thing I've been running
into is that
it doesn't seem to work properly with a <xsl:sort> sorted data set...
Nothing I've
tried using this method seemed to do the trick. :(

As I said before, nothing will work if you do not include the sorted nodes
in an RTF and transform them to a temporary tree. Then you can perform the
Muenchian method on this temporary tree.


Cheers,
Dimitre Novatchev.
 
D

Dimitre Novatchev

As I said before, nothing will work if you do not include the sorted nodes
in an RTF and transform them to a temporary tree. Then you can perform the
Muenchian method on this temporary tree.

Actually, you'll be able to:

1. Perform the Muenchian method (no sorting is necessary).

2. Do the sorting


So, this is a very simple exercise in grouping.

This transformation:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:eek:utput omit-xml-declaration="yes" indent="yes"/>

<xsl:key name="kGN" match="gamename" use="." />

<xsl:template match="/">
<games>
<xsl:for-each select=
"/*/*/gamename[generate-id()
=
generate-id(key('kGN',.)[1])
]">
<xsl:sort/>
<xsl:copy-of select=".."/>
</xsl:for-each>
</games>
</xsl:template>
</xsl:stylesheet>


when applied on your original source xml document:

<GameSets>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>10th Anniversary</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Anarchs</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Ancient Hearts</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Bloodlines</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Camarilla</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Netrunner</gamename>
<setname>Classic</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Dark Sovereigns</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Final Nights</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Gehenna</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Jyhad</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Kindred Most Wanted</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Legacies of Blood</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Netrunner</gamename>
<setname>Netrunner</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Nights of Reckoning</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Promotional</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Netrunner</gamename>
<setname>Proteus</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Sabbat</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>Sabbat War</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>The Black Hand</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>V:TES</setname>
</GameSetsRow>
</GameSets>

produces the desired sorted output for all (2 in this case) uniquely named
games:

<games>
<GameSetsRow>
<gamename>Netrunner</gamename>
<setname>Classic</setname>
</GameSetsRow>
<GameSetsRow>
<gamename>Vampire: The Eternal Struggle</gamename>
<setname>10th Anniversary</setname>
</GameSetsRow>
</games>

Hope this helped.


Cheers,
Dimitre Novatchev
 
J

Joe Kesselman

Dimitre said:
As I said before, nothing will work if you do not include the sorted nodes
in an RTF and transform them to a temporary tree. Then you can perform the
Muenchian method on this temporary tree.

Yep, that's the right approach.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,816
Latest member
SapanaCarpetStudio

Latest Threads

Top