RE: [xsl] Key problem. Ordering XML with conditions than generate new associations.

Subject: RE: [xsl] Key problem. Ordering XML with conditions than generate new associations.
From: "Gabriel Osorio" <gosorio@xxxxxxxxxxx>
Date: Mon, 3 Oct 2005 09:09:46 -0500
Thanks a lot Geert.

I have been studying your comments carefully. You show me several aspects
that were unknown for me.

I have other tasks now. After I have correct my style sheet with your
suggestions and I do revision the recommended links maybe I need ask again.

Thank you again because you took the time to check point by point my style
sheet.

Gabriel


-----Original Message-----
From: Geert Josten [mailto:Geert.Josten@xxxxxxxxxxx] 
Sent: Wednesday, September 28, 2005 3:42 PM
To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
Subject: Re: [xsl] Key problem. Ordering XML with conditions than generate
new associations.

Hi,

Gabriel Osorio wrote:

> About: "RE: [xsl] Another <xsl:key> problem "
> 
> My intentions are educational. :-) I'm a beginner.

I thought so, that is why I asked you to start a new thread...

> Now my problem is to order nodes in groups based on certain
characteristics
> of their children. (Using DIV tags.) And perhaps seeing to other way I can
> find my own solution.

You found a complex example.. :-)

There are several references to tutorials on http://www.w3.org/Style/XSL/,
among which 
http://www.dpawson.co.uk/xsl/xslfaq.html...

> Really I do not have opinion. I used your template and did not obtain
> results, because of it I sent the xml again.

Many templates have been sent. You mean mine? The one that was generating a
XSL in ax-xsl namespace?

> Your recursion solution is elegant, but I can't understand the siblings
use.

That can't have been mine. :-) Who are you referring to? Not very relevant
actually.

> May be you can help me...
> 
> ____________________________________________________________
> My own problem is:
> 
> This is language's teacher board offer example. I need sort teacher's
offers
> by price. And sort teachers from cheapest to most expensive. 
> 
> With this rule: "If the course can pay with credit card or is not
informed,
> it's a promotion." 
> 
> The promotions are a new group. And carry out the teacher's rules.
> 
> Other additional complication: courses have two or more teachers. And the
> sorted group uses the first teacher's name.

It appears to me that your xml is already sorted to price, though empty pay
and Credit pay 
grades/courses are not separated into a new group (grade?).

If you would just like to sort the grades to price, you could do the
following:

<!-- promotions -->
<xsl:apply-templates select="//grade[(var/pay = 'Credit') or (var/pay =
'')]" />
<!-- others -->
<xsl:apply-templates select="//grade[(var/pay != 'Credit') and (var/pay !=
'')]">
   <xsl:sort select="var/price" data-type="number" />
</xsl:apply-templates>

No need for keys in that case.

Reordering the courses along to teachers is more difficult as it requires
regrouping the 
information. This can be done like this:

<xsl:key name="courses-by-teacher" match="course" use="teacher" />
<xsl:key name="teachers" match="teacher" use="." />

<xsl:for-each select="//teacher">
   <!-- alphabetize them -->
   <xsl:sort />

   <!-- teacher occur multiple times, take first occurrence -->
   <xsl:if test="generate-id(.) = generate-id(key('teachers', .)[1])">
     <group>
       <xsl:copy-of select="." />
       <xsl:copy-of select="key('courses-by-teacher', .)" />
     </group>
   </xsl:if>
</xsl:for-each>

<snip />

> --------------------------------------------------
> My XSL is:
> --------------------------------------------------
> <?xml version="1.0"?>
> <xsl:stylesheet version="1.0"
> xmlns:xsl="http://www.w3.org/1999/XSL/Transform";> 

> <xsl:key name="order1" match="grade" use="((substring(var/pay,1,1))='') or
> ((substring(var/pay,1,1))='C')" />
> <xsl:key name="order2" match="grade"
> use="concat(((substring(var/pay,1,1))='') or
> ((substring(var/pay,1,1))='C'),course[1]/teacher)" />

Try to use key names that explain what is matched and what is used as key.

Besides, I think I would move the boolean to a predicate in the match
pattern and define separate 
keys for the true and the false case. Why using a boolean where a string is
expected?

Looking more carefully at order1, I think you could have replaced it with a
(global) variable just 
as well:

<xsl:variable name="promotions"
select="//grade[((substring(var/pay,1,1))='') or 
((substring(var/pay,1,1))='C')]" />

Doesn't that make much more sense?

> <xsl:template match='root'> 
>   <table>
>   <!--Expecial logic for C pay nodes.-->

>     <xsl:for-each select="grade[count(. | key('order1', 'true')[1]) = 1]">
>       <xsl:if test="generate-id()=generate-id(key('order1', 'true'))">

I am pretty certain that the predicate in the select of this for-each is
doing the same as the test 
in the if. They are both selecting the first in document order that matches
the given key 'true', 
which refers to the promotion courses (or grades?) I guess.

>       <tr><td><div><xsl:attribute
> name="id">credit_offer</xsl:attribute><table>
>       <tr><td>Credit Offer</td></tr>
>       <xsl:for-each select="key('order1', 'true')">
>             <xsl:sort select="var/price" order="ascending"
> data-type="number"/>
>                   <xsl:call-template name="course"/>
>         </xsl:for-each>
>       </table></div></td></tr>
>         </xsl:if>
>     </xsl:for-each>
>   <!--Logic to group nodes with standar pays-->

>     <xsl:for-each select="grade[count(. | key('order2',
> concat('false',course[1]/teacher))[1]) = 1]">
>     <xsl:if test="generate-id()=generate-id(key('order2',
> concat('false',course[1]/teacher)))">

Here again.

>       <tr><td><div><xsl:attribute name="id"><xsl:value-of
> select="course[1]/teacher"/></xsl:attribute><table>
>       <tr><td>Teacher's Leader: <xsl:value-of
> select="course[1]/teacher"/></td></tr>
>       <xsl:for-each select="key('order2',
> concat('false',course[1]/teacher))">

Ah, this makes sense. You are picking the courses that belong to a certain
teacher, right? A good 
use case for a key. Though, how is this related to credit/non-credit
courses? It is not obvious that 
you have limited your expression to the credit courses (or are it grades?)
by the 'false'. This 
becomes much more obvious when using separate keys for credit and non-credit
courses...

>         <xsl:sort select="var/price" order="ascending"
data-type="number"/>
>                   <xsl:call-template name="course"/>
>         </xsl:for-each>
>         </table></div></td></tr>
>       </xsl:if>
>     </xsl:for-each>
>   </table>
> </xsl:template>
> <!--The rest.-->
> <xsl:template name="course"> 
>   <tr>
>     <td><xsl:value-of select="position()"/>.</td>
>     <td>Price: <xsl:value-of select="var/price"/>.</td>
>     <td><xsl:apply-templates select='course'/></td>
>   </tr>
> </xsl:template>
> <xsl:template match="course">
>     <br/>teacher: <xsl:value-of select="teacher"/>&#xa0;&#xa0;
>     class: <xsl:value-of select="class"/>&#xa0;&#xa0;
>     shift: <xsl:value-of select="shift"/>&#xa0;&#xa0;
>     Pay: <xsl:value-of select="../var/pay"/>
> </xsl:template>
> </xsl:stylesheet>

Try to keep your solution as plain and simple as possible. It should be
obvious from naming and 
expressions what you are trying to achieve.

HTH,
Geert

Current Thread