Reputation: 383
Consider a case I have to get the count of passed students, he is considered passed if he passes all the exams.
<testResults version="1.2">
<student test="1" pass="true" name="A"></student>
<student test="2" pass="true" name="A"></student>
<student test="1" pass="false" name="B"></student>
<student test="2" pass="true" name="B"></student>
<student test="1" pass="false" name="C"></student>
<student test="2" pass="false" name="C"></student>
<student test="1" pass="true" name="D"></student>
<student test="2" pass="true" name="D"></student>
</testResults>
I want to get the count of students who passed all subjects. How do I do that? I got a method where I iterate through all the students and display who passed all but how do I get the count of all students.
I'm using ,
<xsl:for-each select="/testResults/student/[not(@name = preceding::*/@name)]">
<xsl:variable name="allFailureCount" select="count(/testResults/*[attribute::pass='false'][@tn = current()/@name])" />
<xsl:choose>
<xsl:when test="$allFailureCount > 0"></xsl:when>
<xsl:otherwise><xsl:value-of select="@name" /></xsl:otherwise>
</xsl:choose>
</xsl:for-each>
Upvotes: 0
Views: 6783
Reputation: 70648
What you need here is a count of the "distinct" of student names, for students who have passed all their exams. It would probably help if you defined a key to look up the tests
<xsl:key name="students" match="student" use="@name" />
Then, to get the distinct list of passing students, you can use distinct-values
in XSLT 2.0
distinct-values(testResults/student[not(key('students', @name)/@pass='false')]/@name)
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text" />
<xsl:key name="students" match="student" use="@name" />
<xsl:template match="/">
<xsl:variable name="passedStudents" select="distinct-values(testResults/student[not(key('students', @name)/@pass='false')]/@name)" />
<xsl:for-each select="$passedStudents">
<xsl:text>Student </xsl:text><xsl:value-of select="." /><xsl:text> </xsl:text>
</xsl:for-each>
<xsl:text>Total </xsl:text>
<xsl:value-of select="count($passedStudents)" />
</xsl:template>
</xsl:stylesheet>
Upvotes: 1
Reputation: 2734
Something like this may work
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<result>
<xsl:variable name="prods" select="testResults/student/name" />
<xsl:for-each select="$prods">
<xsl:if test="generate-id() = generate-id($prods[. = current()][1])">
<xsl:if test="count(/testResults/student[@pass ='true' and name=current()]) = 2">
<name>
<xsl:value-of select="." />
</name>
</xsl:if>
</xsl:if>
</xsl:for-each>
</result>
</xsl:template>
</xsl:stylesheet>
Output
<result>
<name>A</name>
<name>D</name>
</result>
First it constructs list of unique names using approach from How to use XSLT to create distinct values - and then counts number of nodes that match 2 conditions - pass is true and name is equal current unique name. If count is equal to the number of tests (2 - I set it hard-coded) - the name is added to the output.
EDIT
To count names - you can store them into temp var and then use simple count
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<xsl:variable name="prods" select="testResults/student/name" />
<xsl:variable name="passOne">
<xsl:for-each select="$prods">
<xsl:if test="generate-id() = generate-id($prods[. = current()][1])">
<xsl:if test="count(/testResults/student[@pass ='true' and name=current()]) = 2">
<name>
<xsl:value-of select="." />
</name>
</xsl:if>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="count($passOne/name)" />
</xsl:template>
</xsl:stylesheet>
Upvotes: 0