I am writing a program, that will divide my students into groups. Lets say the groups are A, B, C and D. In each group there can be only so many students. Those numbers I have in a global variable:
<xsl:variable name="volnemiesta" select="16, 15, 30, 0"/>.
The students can pick their group as preferences. So one can pick A,B,C,D, another B,C,D,A. Each student has a score. The students are divided according the score, those with higher score get to pick first, those with lower score later. Each student get into the group which is sooner in his preferences and has space.
I need to remember how many student are already in each group so the program can assign students only to not full groups. The main part of my program is as follows:
<xsl:variable name="volnemiesta" select="16, 15, 30, 0"/>
<xsl:template match="/triedy">
<xsl:variable name="zameraniavsetky" select="string-join((trieda/student/@vyber)[. != ''], ',')"/>
<xsl:variable name="zamerania">
<xsl:perform-sort select="distinct-values(tokenize($zameraniavsetky, ','))">
<xsl:sort select="."/>
</xsl:perform-sort>
</xsl:variable>
<xsl:result-document href="./zoznam.txt" method="text">
<xsl:for-each select="trieda/student">
<xsl:sort select="@priemer = ''"/>
<xsl:sort select="@priemer" data-type="number"/>
<xsl:sort select="@priezvisko"/>
<xsl:sort select="@meno"/>
<xsl:variable name="zameranie" select="my:prirad(tokenize(@vyber, ','), $volnemiesta)"/>
<xsl:variable name="volnemiesta" select="my:zmen($zameranie, $volnemiesta)"/>
<xsl:apply-templates select=".">
<xsl:with-param name="zameranie" select="$zameranie"/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:result-document>
</xsl:template>
The line
<xsl:variable name="zameranie" select="my:prirad(tokenize(@vyber, ','), $volnemiesta)"/>
calculates the group assigned to the student based on his preferences and number of free places in the group.
And this line:
<xsl:variable name="volnemiesta" select="my:zmen($zameranie, $volnemiesta)"/>
should compute the changed free space in the groups. And here is the problem. I know I cannot change the value of a variable. How can I do that?
Update 1
So here is my full stylesheet. I think I do something wrong in the my:zmen function, because after the computation the error is
Error at char 34 in expression in xsl:accumulator-rule/@select on line 8 column 116 of zamerania.xsl: XTTE0570 Cannot convert string "15 15 30 0" to an integer.
<?xml version="1.1" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xml:lang="sk"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="http://www.spsjm.sk">
<xsl:accumulator name="volnemiesta" as="xs:integer+" initial-value="16, 15, 30, 0">
<xsl:accumulator-rule match="trieda/student" select="my:zmen(my:prirad(tokenize(@vyber, ','), $value), $value)"/>
</xsl:accumulator>
<xsl:mode use-accumulators="volnemiesta"/>
<xsl:template match="text()"/>
<xsl:template match="/triedy">
<xsl:variable name="zameraniavsetky" select="string-join((trieda/student/@vyber)[. != ''], ',')"/>
<xsl:variable name="zamerania">
<xsl:perform-sort select="distinct-values(tokenize($zameraniavsetky, ','))">
<xsl:sort select="."/>
</xsl:perform-sort>
</xsl:variable>
<xsl:result-document href="./zoznam.txt" method="text">
<xsl:apply-templates select="trieda/student">
<xsl:sort select="@priemer = ''"/>
<xsl:sort select="@priemer" data-type="number"/>
<xsl:sort select="@priezvisko"/>
<xsl:sort select="@meno"/>
</xsl:apply-templates>
</xsl:result-document>
</xsl:template>
<xsl:template match="student">
<xsl:variable name="zameranie" select="my:prirad(tokenize(@vyber, ','), accumulator-before('volnemiesta'))"/>
<xsl:value-of select="@meno"/>
<xsl:text>		</xsl:text>
<xsl:value-of select="@priezvisko"/>
<xsl:text>		</xsl:text>
<xsl:value-of select="../@id"/>
<xsl:text>		</xsl:text>
<xsl:value-of select="@priemer"/>
<xsl:text>		</xsl:text>
<xsl:value-of select="@vyber"/>
<xsl:text>		=>		</xsl:text>
<xsl:value-of select="$zameranie"/>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:function name="my:prirad">
<xsl:param name="vybrane"/>
<xsl:param name="volnemiesta"/>
<xsl:variable name="zameranie">
<xsl:choose>
<xsl:when test="$vybrane[1] = 'EEN' and $volnemiesta[1] > 0">
<xsl:text>EEN</xsl:text>
</xsl:when>
<xsl:when test="$vybrane[1] = 'OBZ' and $volnemiesta[2] > 0">
<xsl:text>OBZ</xsl:text>
</xsl:when>
<xsl:when test="$vybrane[1] = 'PIT' and $volnemiesta[3] > 0">
<xsl:text>PIT</xsl:text>
</xsl:when>
<xsl:when test="$vybrane[1] = 'VYE' and $volnemiesta[4] > 0">
<xsl:text>VYE</xsl:text>
</xsl:when>
<xsl:when test="$vybrane[2] = 'EEN' and $volnemiesta[1] > 0">
<xsl:text>EEN</xsl:text>
</xsl:when>
<xsl:when test="$vybrane[2] = 'OBZ' and $volnemiesta[2] > 0">
<xsl:text>OBZ</xsl:text>
</xsl:when>
<xsl:when test="$vybrane[2] = 'PIT' and $volnemiesta[3] > 0">
<xsl:text>PIT</xsl:text>
</xsl:when>
<xsl:when test="$vybrane[2] = 'VYE' and $volnemiesta[4] > 0">
<xsl:text>VYE</xsl:text>
</xsl:when>
<xsl:when test="$vybrane[3] = 'EEN' and $volnemiesta[1] > 0">
<xsl:text>EEN</xsl:text>
</xsl:when>
<xsl:when test="$vybrane[3] = 'OBZ' and $volnemiesta[2] > 0">
<xsl:text>OBZ</xsl:text>
</xsl:when>
<xsl:when test="$vybrane[3] = 'PIT' and $volnemiesta[3] > 0">
<xsl:text>PIT</xsl:text>
</xsl:when>
<xsl:when test="$vybrane[3] = 'VYE' and $volnemiesta[4] > 0">
<xsl:text>VYE</xsl:text>
</xsl:when>
<xsl:when test="$vybrane[4] = 'EEN' and $volnemiesta[1] > 0">
<xsl:text>EEN</xsl:text>
</xsl:when>
<xsl:when test="$vybrane[4] = 'OBZ' and $volnemiesta[2] > 0">
<xsl:text>OBZ</xsl:text>
</xsl:when>
<xsl:when test="$vybrane[4] = 'PIT' and $volnemiesta[3] > 0">
<xsl:text>PIT</xsl:text>
</xsl:when>
<xsl:when test="$vybrane[4] = 'VYE' and $volnemiesta[4] > 0">
<xsl:text>VYE</xsl:text>
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:sequence select="$zameranie"/>
</xsl:function>
<xsl:function name="my:zmen">
<xsl:param name="zameranie"/>
<xsl:param name="volnemiesta"/>
<xsl:variable name="upravenemiesta" as="xs:integer+">
<xsl:choose>
<xsl:when test="$zameranie='EEN'">
<xsl:value-of select="$volnemiesta[1]-1, $volnemiesta[2], $volnemiesta[3], $volnemiesta[4]"/>
</xsl:when>
<xsl:when test="$zameranie='OBZ'">
<xsl:value-of select="$volnemiesta[1], $volnemiesta[2]-1, $volnemiesta[3], $volnemiesta[4]"/>
</xsl:when>
<xsl:when test="$zameranie='PIT'">
<xsl:value-of select="$volnemiesta[1], $volnemiesta[2], $volnemiesta[3]-1, $volnemiesta[4]"/>
</xsl:when>
<xsl:when test="$zameranie='VYE'">
<xsl:value-of select="$volnemiesta[1], $volnemiesta[2], $volnemiesta[3], $volnemiesta[4]-1"/>
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:sequence select="$upravenemiesta"/>
</xsl:function>
</xsl:stylesheet>
Update 2
Sample XML file ziaci.xml.
<triedy xml:lang="sk">
<trieda id="III.C">
<student meno="Adam" priezvisko="Prvý" priemer="1.0" vyber="EEN,PIT,OBZ"/>
<student meno="Bohumil" priezvisko="Druhý" priemer="3.2" vyber="PIT,OBZ,EEN"/>
<student meno="Cyril" priezvisko="Tretí" priemer="1.6" vyber="EEN,OBZ,PIT"/>
<student meno="Daniel" priezvisko="Štvrtý" priemer="1.6" vyber="EEN,PIT,OBZ"/>
<student meno="Emil" priezvisko="Piaty" priemer="2.0" vyber="OBZ,EEN,PIT"/>
</trieda>
<trieda id="III.D">
<student meno="Filip" priezvisko="Šiesty" priemer="2.8" vyber="OBZ,EEN,PIT"/>
<student meno="Gustav" priezvisko="Siedmy" priemer="1.4" vyber="EEN,PIT,OBZ"/>
<student meno="Hugo" priezvisko="Osmy" priemer="1.6" vyber="EEN,PIT,OBZ"/>
<student meno="Ivan" priezvisko="Deviaty" priemer="2.6" vyber="PIT,EEN,OBZ"/>
<student meno="Jano" priezvisko="Desiaty" priemer="2.0" vyber="EEN,PIT,OBZ"/>
</trieda>
</triedy>
volnemiesta,tail students) as inputs, exit whenstudentsis empty, passing updatedvolnemiestafor every nested level).xsl:iterateto sequentially process some items and pass variable/parameter values on to the next processing. As you use stuff likeperform-sortandxsl:result-document, you probably use an XSLT 3 processor like Saxon 12 (Saxon has been an XSLT 3 processor since 9.8), thus, if you want to keep a state and think in terms of loops and variablesxsl:iterateis an option. There also accumulators,fn:fold-leftor recursion in general, be it with functions orapply-templates(where you can also pass parameters)my:prirad()andmy:zmenwhich are your functions for calculating group assignment to a student and also space left in a group. That's taking an iterative and procedural approach, wherein the notion of updating variables as the algorithm proceeds down the list of students. XSLT is not iterative and<xsl:for-each>is not a loop but a mapping. To approach requires thinking of how to fill up each group according to preferences. I'll see if I can't propose a solution.<xsl:variable name="upravenemiesta" as="xs:integer+">, anywhere where you want to return that sequence, don't usexsl:value-of(as that creates a text node), usexsl:sequencee.g.<xsl:sequence select="$volnemiesta[1]-1, $volnemiesta[2], $volnemiesta[3], $volnemiesta[4]"/>