!

Dette materialet blir ikke lenger vedlikeholdt. Du vil finne oppdatert materiale på siden: http://borres.hiof.no/wep/

Sortering
Beregning
Stringer
Børre Stenseth
XSL > XSLT >Sortering

Sortering, stringer og beregninger

Hva
Sortering, stringer og beregninger i XSL

XSL som språk har ikke typer slik som vi kjenner fra de fleste andre språk. Når vi skal lagre og behandle numeriske data må vi ta utgangspunkt i disse dataene som en sekvens med tegn. Det er to oppgaver som krever litt oppmerksomhet i denne forbindelse: sortering og beregning. Begge deler krever at tegnene tolkes på en fornuftig måte.

Vi bruker et lite datasett som eksempel i denne modulen. Dersom du vi eksperimentere med filene, så kopier dem rette av denne HTML-siden. Anta følgende resultatfil fra en konkurranse der deltagerne hver har fått notert to tider (timer,minutter,sekunder). Vi skal etterhvert interessere oss for samlet tid, rekkefølge osv.:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="trans3.xslt"?>
<resultater>
    <deltager>
        <navn>Syversen</navn>
        <t1>1:31:12</t1>
        <t2>1:22:09</t2>
    </deltager>
    <deltager>
        <navn>Andersen</navn>
        <t1>1:30:12</t1>
        <t2>1:12:09</t2>
    </deltager>
</resultater>

Sortering

La oss først se på de grunnleggende sorteringsmulighetene vi har i XSLT. Hvis vi ønsker å lage en HTML-fil som skriver ut deltagerlista alfabetisk, basert på navn, kan vi lage følgende transformasjon:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="html" version="1.0" encoding="ISO-8859-1" indent="yes"/>
   
<xsl:template match="/">
   <html>    <head><title>resultater</title></head>
   <body><h1>Resultater</h1>
   
   <xsl:apply-templates select="/resultater/deltager">
      <xsl:sort select="navn"/>
   </xsl:apply-templates>
   
   </body></html>
</xsl:template>
<xsl:template match="deltager">
   <h3><xsl:value-of select="navn"/></h3>
   <div><xsl:value-of select="t1"/> og <xsl:value-of select="t1"/></div>
</xsl:template>
 
</xsl:stylesheet>
Resultatet http://www.it.hiof.no/~borres/dw/xsl/page3/trans1.html

Hva dersom vi introduserer en blank foran innholdet i navne-elementet til den ene deltageren, Syversen ? Hvis vi kjører den samme transformasjonen vil vi få Syversen foran Andersen. Det blanke tegnet inngår altså i sammenligningen av navne-elemener og forstyrrer ordningen. Vi kan korrigere for dette ved å endre sorteringselementet i transformasjonen slik at omgivende whitespace ignoreres:

<xsl:sort select="normalize-space(navn)"/>

Beregning

La oss se på beregning av samlede resultater. Vi ønsker å finne samlet tid for de to øvelsene for alle deltagerne. Vi har valgt et format for tidsangivelse som er ganske ryddig:

<t1>1:30:12</t1>

og oppgaven lar seg løse med å trekke ut delstringer, tolke dem numerisk og utføre nødvendige beregninger. XSLT gis oss verktøy for dette. En transformasjon som presenterer resultatene med angivelse av samlet tid i antall sekunder kan se slik ut:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="html" version="1.0" encoding="ISO-8859-1" indent="yes"/>
   
<xsl:template match="/">
   <html>   <head><title>resultater</title></head>
   <body><h1>Resultater</h1>
   
   <xsl:apply-templates select="/resultater/deltager"/>
   
   </body></html>
</xsl:template>
<xsl:template match="deltager">
   <h3><xsl:value-of select="navn"/></h3>
   <xsl:variable name="sekundsum" select="3600*(number(substring(t1,1,1))+number(substring(t2,1,1)))  
                              +60*(number(substring(t1,3,2))+number(substring(t2,3,2)))  
                               +(number(substring(t1,6,2))+number(substring(t2,6,2)))"/>
  <div><xsl:value-of select="$sekundsum"/></div>
</xsl:template>
 
</xsl:stylesheet>

Merk at tegnindekseringen begynner på 1, ikke 0 som vi er vant med i andre språk.

Resultatet http://www.it.hiof.no/~borres/dw/xsl/page3/trans3.html

Koden som er foreslått er uten sikkerhetsnett. Hva hvis vi har feil i XML-formatet, eller at en deltager har brukt 10 timer? Det er ganske mye pes å skrive rutiner for datakontroll i XSLT. På den annen side har XSLT en nødutgang som ikke skaper katastrofe i form av programkrasj. Hvis vi introduserer feil format i en av tidene til Andersen, vil vi normalt få følgende resultat: page3/trans3feil.html Vi får altså resultatet NaN (Not a Number). Hvis vi gjør en feil i transformasjonsfila og forsøker å regne på ikke-numeriske tegn eller tegn som vi prøver å hente fra en string med gal indeks får vi samme respons.

Dersom vi bare forsøker å framstille en substring som ikke finnes, f.eks. ved å skrive:

<div><xsl:value-of select="substring(t1,100,20)"/></div>

skjer det ingenting. Ingen feil og ingen string.

Vi ønsker å forbedre vår resultatframstilling noe og vil ha ut det samlede resultatet i samme form som delresultatene i XML-fila, dvs. t:mm.ss. Følgende transformasjon tar seg av dette:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="html" version="1.0" encoding="ISO-8859-1" indent="yes"/>
   
<xsl:template match="/">
   <html>   <head><title>resultater</title></head>
   <body><h1>Resultater</h1>
   
   <xsl:apply-templates select="/resultater/deltager"/>
   
   </body></html>
</xsl:template>
<xsl:template match="deltager">
<h3><xsl:value-of select="navn"/></h3>
   <xsl:variable name="sekundsum" select="3600*(substring(t1,1,1)+substring(t2,1,1))  
                              +60*(substring(t1,3,2)+substring(t2,3,2))  
                              +(substring(t1,6,2)+substring(t2,6,2))"/>
   <xsl:variable name="timer" 
      select ="floor($sekundsum div 3600)"/>
   <xsl:variable name="minutter" 
      select="floor(($sekundsum - $timer * 3600) div 60)"/>
   <xsl:variable name="sekunder" 
      select="$sekundsum -($timer * 3600) -($minutter*60)"/>
   <div>
      <xsl:value-of select="concat($timer,':',$minutter,':',$sekunder)"/>
   </div>
</xsl:template>
 
</xsl:stylesheet>

Vi har brukt funksjonen floor() som runder ned til nærmeste hele tall og funksjonen concat() som slår sammen stringer.

Resultatet http://www.it.hiof.no/~borres/dw/xsl/page3/trans4.html

Merk at vi har forenklet uttrykket ved at vi har fjernet kallene på number()- funksjonen. Dette kan vi gjøre siden XSL-er i stand til å tolke talltegn som numeriske verdier i et numerisk uttrykk.

Beregning og sortering

Vi ønsker nå å få sortert resultatlista etter den samlede tiden. Vi må altså lage et sorteringskriterium som er basert på en beregnet verdi. Dette fungerer:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="html" version="1.0" encoding="ISO-8859-1" indent="yes"/>
   
<xsl:template match="/">
   <html>   <head><title>resultater</title></head>
   <body><h1>Resultater</h1>
   
   <xsl:apply-templates select="/resultater/deltager">
   <xsl:sort select="3600*(substring(t1,1,1)+substring(t2,1,1))  
                              +60*(substring(t1,3,2)+substring(t2,3,2))  
                              +(substring(t1,6,2)+substring(t2,6,2))" 
   order="ascending" data-type="number"/>
   </xsl:apply-templates>
   
   </body></html>
</xsl:template>
<xsl:template match="deltager">
   <xsl:variable name="sumsek" select="3600*(substring(t1,1,1)+substring(t2,1,1))  
                              +60*(substring(t1,3,2)+substring(t2,3,2))  
                              +(substring(t1,6,2)+substring(t2,6,2))"/>   
   <h3><xsl:value-of select="navn"/></h3>
   
   <xsl:variable name="timer" 
      select="floor($sumsek div 3600)"/>
   <xsl:variable name="minutter" 
      select="floor(($sumsek - $timer * 3600) div 60)"/>
   <xsl:variable name="sekunder" 
      select="$sumsek -($timer * 3600) -($minutter*60)"/>
   <div>
      <xsl:value-of 
      select="concat($timer,':',$minutter,':',$sekunder)"/>
   </div>
</xsl:template>
 
</xsl:stylesheet>
Resultatet http://www.it.hiof.no/~borres/dw/xsl/page3/trans5.html

Det virker ganske klønete å regne ut sekundsummen to ganger, først som sortingskriterium og deretter i forbindelse med presentasjonen. Se om du kan finne på noe bedre og fortell meg om det.

Referanser
Vedlikehold
Børre Stenseth, april 2003
( Velkommen ) XSL > XSLT >Sortering ( Løkker )