Wrox Programmer Forums

Need to download code?

View our list of code downloads.

Go Back   Wrox Programmer Forums > XML > XSLT
Password Reminder
Register
| FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read
XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of tens of thousands of software programmers and website developers including Wrox book authors and readers. As a guest, you can read any forum posting. By joining today you can post your own programming questions, respond to other developers’ questions, and eliminate the ads that are displayed to guests. Registration is fast, simple and absolutely free .
DRM-free e-books 300x50
Reply
 
Thread Tools Search this Thread Display Modes
  #1 (permalink)  
Old September 30th, 2003, 07:19 AM
Authorized User
 
Join Date: Jun 2003
Location: , , United Kingdom.
Posts: 14
Thanks: 0
Thanked 0 Times in 0 Posts
Default alternate ordering

I need to order an XML data set based on one node, but rather than ascending or decending, i need to alternate between all the possibilities.

For example, instaed of 1111222233334444 or 4444333322221111, I need the order to be 1234123412341234.

I can only think of very clumsy ways to achieve this. Does anone have a neat way?
Reply With Quote
  #2 (permalink)  
Old September 30th, 2003, 07:52 AM
Friend of Wrox
 
Join Date: Jun 2003
Location: , , United Kingdom.
Posts: 1,212
Thanks: 0
Thanked 1 Time in 1 Post
Default

Hmm sounds interesting, but for me there is not enough detail here to know precisely what you want. Can you post sample input XML and desired output?
Reply With Quote
  #3 (permalink)  
Old September 30th, 2003, 09:08 AM
Authorized User
 
Join Date: Jun 2003
Location: , , United Kingdom.
Posts: 14
Thanks: 0
Thanked 0 Times in 0 Posts
Default

OK, for example I want to order the following XML

<persons>
<person><name>Al</name><number>1</number></person>
<person><name>Bill</name><number>1</number></person>
<person><name>Cal</name><number>2</number></person>
<person><name>Dolly</name><number>2</number></person>
<person><name>Elly</name><number>3</number></person>
<person><name>Gill</name><number>3</number></person>
<person><name>Holly</name><number>4</number></person>
</persons>

by the number node, as follows

<persons>
<person><name>Al</name><number>1</number></person>
<person><name>Cal</name><number>2</number></person>
<person><name>Elly</name><number>3</number></person>
<person><name>Holly</name><number>4</number></person>
<person><name>Bill</name><number>1</number></person>
<person><name>Dolly</name><number>2</number></person>
<person><name>Gill</name><number>3</number></person>
</persons>
Reply With Quote
  #4 (permalink)  
Old September 30th, 2003, 02:10 PM
planoie's Avatar
Friend of Wrox
Points: 16,481, Level: 55
Points: 16,481, Level: 55 Points: 16,481, Level: 55 Points: 16,481, Level: 55
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Aug 2003
Location: Clifton Park, New York, USA.
Posts: 5,407
Thanks: 0
Thanked 16 Times in 16 Posts
Default

Looks to me like you are going to have to come up with something yourself. I've never heard of a programmatic construct in any language that does that.

You're going to have to do something like create multiple datasets, one for each number, then sort each of those by name, then go thru each dataset and element to get them in the right fashion. Could be very tricky if you don't have a fixed number of "number"s.

Peter
Reply With Quote
  #5 (permalink)  
Old October 1st, 2003, 03:29 AM
Friend of Wrox
 
Join Date: Jun 2003
Location: Nor Hachin, Kotayk, Armenia.
Posts: 147
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via Yahoo to armmarti
Default

I can't exactly know what you mean by "neat", but in any case the algorithm is simple and I don't see here any complexity.
You need to group (or just sort, since you have numbers, but I used grouping which can be helpful when the "number" element will be changed) and then for every group output i-th person. Honestly, I couldn't find simpler solution (I mean some elegant XSLT-specific construct or approach, although in the code below "person[position() = $current-offset]" XPath is very convenient in this situation when the number of persons in each group is not the same)...

Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="number-to-person" match="person" use="number"/>
    <xsl:template match="/">
        <xsl:variable name="groups">
            <xsl:for-each select="/persons/person[generate-id() = generate-id(key('number-to-person', number)[1])]">
                <xsl:sort select="number"/>
                <xsl:variable name="current-group" select="/persons/person[number = current()/number]"/>
                <group>
                    <count>
                        <xsl:value-of select="count($current-group)"/>
                    </count>
                    <xsl:for-each select="$current-group">
                        <xsl:copy-of select="."/>
                    </xsl:for-each>
                </group>
            </xsl:for-each>
        </xsl:variable>

        <xsl:variable name="max-count">
            <xsl:call-template name="get-max">
                <xsl:with-param name="set" select="$groups/*"/>
            </xsl:call-template>
        </xsl:variable>

        <persons>
            <xsl:call-template name="serialize">
                <xsl:with-param name="set" select="$groups/*"/>
                <xsl:with-param name="max-group-count" select="$max-count"/>
            </xsl:call-template>
        </persons>    
    </xsl:template>

    <xsl:template name="serialize">
        <xsl:param name="set"/>
        <xsl:param name="current-offset" select="1"/>
        <xsl:param name="max-group-count"/>

        <xsl:choose>
            <xsl:when test="$max-group-count &lt; $current-offset"/>
            <xsl:otherwise>
                <xsl:for-each select="$set">
                    <xsl:copy-of select="person[position() = $current-offset]"/>
                </xsl:for-each>
                <xsl:call-template name="serialize">
                    <xsl:with-param name="set" select="$set"/>
                    <xsl:with-param name="current-offset" select="$current-offset + 1"/>
                    <xsl:with-param name="max-group-count" select="$max-group-count"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template name="get-max">
        <xsl:param name="set"/>

        <xsl:choose>
            <xsl:when test="$set">
                <xsl:variable name="max-of-rest">
                    <xsl:call-template name="get-max">
                        <xsl:with-param name="set" select="$set[position() > 1]"/>
                    </xsl:call-template>
                </xsl:variable>
                <xsl:choose>
                    <xsl:when test="$set[1]/count &lt; $max-of-rest">
                        <xsl:value-of select="$max-of-rest"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select="$set[1]/count"/>
                    </xsl:otherwise> 
                </xsl:choose>
            </xsl:when>
            <xsl:otherwise>0</xsl:otherwise>
        </xsl:choose> 
    </xsl:template>
</xsl:stylesheet>
I used Saxon 6.5.2, which performs RTF to nodeset conversion implicitly if the version is set to 1.1; you may need to perform the conversion explicitly by using the EXSLT's node-set function or something similar.

Regards,
Armen
Reply With Quote
  #6 (permalink)  
Old October 1st, 2003, 03:38 AM
Friend of Wrox
 
Join Date: Jun 2003
Location: , , United Kingdom.
Posts: 1,212
Thanks: 0
Thanked 1 Time in 1 Post
Default

How about a simple recursive template like this one:
Code:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml"/>
    <xsl:variable name="numRecs" select="count(/persons/person)"/>
    <xsl:key name="nodesByNumber" match="person" use="number"/>
    <xsl:template match="/">
        <persons>
            <xsl:call-template name="genOutput">
                <xsl:with-param name="num" select="1"/>
                <xsl:with-param name="posn" select="1"/>
                <xsl:with-param name="recCount" select="1"/>
            </xsl:call-template>
        </persons>
    </xsl:template>

    <xsl:template name="genOutput">
        <xsl:param name="num"/>
        <xsl:param name="posn"/>
        <xsl:param name="recCount"/>
        <xsl:choose>
            <xsl:when test="$recCount &gt; $numRecs">

            </xsl:when>
            <xsl:when test="key('nodesByNumber', $num)[$posn]">
                <!-- if a node with the given number and position exists, then
                copy it to result tree and move on to the next number -->
                <xsl:copy-of select="key('nodesByNumber', $num)[$posn]"/>
                <xsl:call-template name="genOutput">
                    <xsl:with-param name="num" select="$num+1"/>
                    <xsl:with-param name="posn" select="$posn"/>
                    <xsl:with-param name="recCount" select="$recCount+1"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <!-- if a node with the given number and position doesn't exist, then
                move back to number 1 and increment the position -->
                <xsl:call-template name="genOutput">
                    <xsl:with-param name="num" select="1"/>
                    <xsl:with-param name="posn" select="$posn+1"/>
                    <xsl:with-param name="recCount" select="$recCount"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

</xsl:stylesheet>
The basic idea is that a key is defined to group the <person> nodes into sets which have the same <number>. Then I use two params to keep track of the number and position within that group of numbers, e.g. start at num=1, posn=1 then when we get to num=5 posn=1 it finds that no such node exists, so it tries again with num=1 posn=2, etc. (Edit: <s>It will keep going until all records are processed, so it should not matter if your numbers are not sequential, nor should it matter if the groups are uneven in number.</s> Actually, that's complete rubbish, the truth is the exact opposite )

Maybe this is an oversimplification of your problem?

hth
Phil
Reply With Quote
  #7 (permalink)  
Old October 1st, 2003, 09:28 AM
Authorized User
 
Join Date: Jun 2003
Location: , , United Kingdom.
Posts: 14
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Thanks guys, both solutions were great!
Reply With Quote
  #8 (permalink)  
Old October 1st, 2003, 11:51 AM
Authorized User
 
Join Date: Jun 2003
Location: , , United Kingdom.
Posts: 14
Thanks: 0
Thanked 0 Times in 0 Posts
Default

OK.. maybe I spoke too soon!

pgtips, I'm using your solution as I found it easier to follow and modify. It doesn't seem to work if one of the "numbers" is not present in the recordset... I'm thinking I need to somehow tell it to go onto the next one if it doesn't find a particular number, but then I also need it to remember that that number wasn't there so it doesn't search the whole recorset every time... any ideas?

Cheers
Reply With Quote
  #9 (permalink)  
Old October 2nd, 2003, 02:39 AM
Friend of Wrox
 
Join Date: Jun 2003
Location: , , United Kingdom.
Posts: 1,212
Thanks: 0
Thanked 1 Time in 1 Post
Default

Yes I noticed that after I posted it, and edited my original post accordingly. Probably the best way is to include armen's template which calculates the max number, and use that max to decide when to increment the position, rather than my original method of incrementing it when a node is not found.

Alternatively, you could just use armen's method instead. The principle is the same as my attempt (i.e. a recursive template that moves through the person nodes in alternate order), it just uses a different method to group the nodes by number.
Reply With Quote
  #10 (permalink)  
Old October 2nd, 2003, 04:38 AM
Authorized User
 
Join Date: Jun 2003
Location: , , United Kingdom.
Posts: 14
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Yes you're right, the other solution does allow numbers to be missing.

I can't get it to work with MSXML4 though (works fine if I just view it with Netscape). Never mind, I will bash on.

Thanks guys.
Reply With Quote
Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off
Trackbacks are Off
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
alternate method of using MapQuest tennisdad1 Javascript How-To 2 January 23rd, 2006 08:06 PM
Ordering rows problem ami4quest6 SQL Language 10 October 12th, 2005 01:13 AM
Ordering a dataview Louisa VB.NET 2002/2003 Basics 1 November 11th, 2004 10:04 AM
Ordering results EstherMStrom XSLT 6 October 13th, 2004 09:50 AM
Ordering lists POL XSLT 3 June 9th, 2003 11:39 PM



All times are GMT -4. The time now is 06:16 PM.


Powered by vBulletin®
Copyright ©2000 - 2019, Jelsoft Enterprises Ltd.
© 2013 John Wiley & Sons, Inc.