So I'm working with an xml of hockey statistics that is generated by a separate program. In generates a node for each player, with sub nodes for each season (plus one for career total), with sub nodes in each season for the type of stats, and then values within those sub nodes.
Sample of XML:
<player name="Player A" checkname="A,PLAYER">
<stats year="2017-18" gp="30">
<shots g="3" a="1" pts="4" sh="23" pct=".130" ps="0" psatt="0"></shots>
<goaltype gw="0" pp="0" sh="0" ua="0" fg="0" ot="0" en="0" hat="0" gt="0" so="0"></goaltype>
<penalty count="0" minutes="0" minor="0" major="0" misc10="0" miscgame="0" miscgross="0" match="0"></penalty>
<misc plus="10" minus="9" plusminus="+1" facewon="67" facelost="71" facepct=".486" blk="17"></misc>
</stats>
<stats year="2018-19" gp="37">
<shots g="5" a="5" pts="10" sh="28" pct=".179" ps="0" psatt="0"></shots>
<goaltype gw="0" pp="0" sh="1" ua="0" fg="0" ot="0" en="0" hat="0" gt="0" so="0"></goaltype>
<penalty count="2" minutes="4" minor="2" major="0" misc10="0" miscgame="0" miscgross="0" match="0"></penalty>
<misc plus="15" minus="13" plusminus="+2" facewon="118" facelost="106" facepct=".527" blk="23"></misc>
</stats>
<stats year="2019-20" gp="3">
<shots g="2" a="0" pts="2" sh="6" pct=".333" ps="0" psatt="0"></shots>
<goaltype gw="0" pp="1" sh="0" ua="0" fg="0" ot="0" en="0" hat="0" gt="0" so="0"></goaltype>
<penalty count="0" minutes="0" minor="0" major="0" misc10="0" miscgame="0" miscgross="0" match="0"></penalty>
<misc plus="1" minus="2" plusminus="-1" facewon="36" facelost="30" facepct=".545" blk="3"></misc>
</stats>
<stats year="TOTAL" gp="70">
<shots g="10" a="6" pts="16" sh="57" pct=".175" ps="0" psatt="0"></shots>
<goaltype gw="0" pp="1" sh="1" ua="0" fg="0" ot="0" en="0" hat="0" gt="0" so="0"></goaltype>
<penalty count="2" minutes="4" minor="2" major="0" misc10="0" miscgame="0" miscgross="0" match="0"></penalty>
<misc plus="26" minus="24" plusminus="+2" facewon="221" facelost="207" facepct=".516" blk="43"></misc>
</stats>
</player>
<player name="Player B" checkname="B,PLAYER">
<stats year="2016-17" gp="37">
<shots g="1" a="11" pts="12" sh="24" pct=".042" ps="0" psatt="0"></shots>
<goaltype gw="0" pp="0" sh="0" ua="0" fg="0" ot="0" en="0" hat="0" gt="0" so="0"></goaltype>
<penalty count="7" minutes="14" minor="7" major="0" misc10="0" miscgame="0" miscgross="0" match="0"></penalty>
<misc plus="33" minus="26" plusminus="+7" facewon="1" facelost="0" facepct="1.000" blk="54"></misc>
</stats>
<stats year="2017-18" gp="36">
<shots g="3" a="14" pts="17" sh="47" pct=".064" ps="0" psatt="0"></shots>
<goaltype gw="0" pp="1" sh="0" ua="0" fg="0" ot="0" en="0" hat="0" gt="0" so="0"></goaltype>
<penalty count="8" minutes="16" minor="8" major="0" misc10="0" miscgame="0" miscgross="0" match="0"></penalty>
<misc plus="31" minus="34" plusminus="-3" facewon="0" facelost="1" facepct=".000" blk="43"></misc>
</stats>
<stats year="2018-19" gp="37">
<shots g="3" a="13" pts="16" sh="44" pct=".068" ps="0" psatt="0"></shots>
<goaltype gw="0" pp="0" sh="0" ua="0" fg="0" ot="0" en="0" hat="0" gt="0" so="0"></goaltype>
<penalty count="5" minutes="10" minor="5" major="0" misc10="0" miscgame="0" miscgross="0" match="0"></penalty>
<misc plus="40" minus="36" plusminus="+4" facewon="1" facelost="0" facepct="1.000" blk="47"></misc>
</stats>
<stats year="2019-20" gp="3">
<shots g="0" a="0" pts="0" sh="2" pct=".000" ps="0" psatt="0"></shots>
<goaltype gw="0" pp="0" sh="0" ua="0" fg="0" ot="0" en="0" hat="0" gt="0" so="0"></goaltype>
<penalty count="2" minutes="4" minor="2" major="0" misc10="0" miscgame="0" miscgross="0" match="0"></penalty>
<misc plus="1" minus="4" plusminus="-3" facewon="0" facelost="0" facepct=".000" blk="4"></misc>
</stats>
<stats year="TOTAL" gp="113">
<shots g="7" a="38" pts="45" sh="117" pct=".060" ps="0" psatt="0"></shots>
<goaltype gw="0" pp="1" sh="0" ua="0" fg="0" ot="0" en="0" hat="0" gt="0" so="0"></goaltype>
<penalty count="22" minutes="44" minor="22" major="0" misc10="0" miscgame="0" miscgross="0" match="0"></penalty>
<misc plus="105" minus="100" plusminus="+5" facewon="2" facelost="1" facepct=".667" blk="148"></misc>
</stats>
</player>
<player name="Player C" checkname="C,PLAYER">
<stats year="2017-18" gp="25">
<shots g="1" a="3" pts="4" sh="25" pct=".040" ps="0" psatt="0"></shots>
<goaltype gw="0" pp="0" sh="0" ua="0" fg="0" ot="0" en="0" hat="0" gt="0" so="0"></goaltype>
<penalty count="4" minutes="19" minor="2" major="1" misc10="0" miscgame="1" miscgross="0" match="0"></penalty>
<misc plus="2" minus="8" plusminus="-6" facewon="1" facelost="5" facepct=".167" blk="14"></misc>
</stats>
<stats year="2018-19" gp="33">
<shots g="3" a="2" pts="5" sh="47" pct=".064" ps="0" psatt="0"></shots>
<goaltype gw="1" pp="0" sh="0" ua="1" fg="0" ot="0" en="0" hat="0" gt="0" so="0"></goaltype>
<penalty count="7" minutes="14" minor="7" major="0" misc10="0" miscgame="0" miscgross="0" match="0"></penalty>
<misc plus="5" minus="11" plusminus="-6" facewon="3" facelost="5" facepct=".375" blk="25"></misc>
</stats>
<stats year="2019-20" gp="3">
<shots g="0" a="1" pts="1" sh="3" pct=".000" ps="0" psatt="0"></shots>
<goaltype gw="0" pp="0" sh="0" ua="0" fg="0" ot="0" en="0" hat="0" gt="0" so="0"></goaltype>
<penalty count="1" minutes="2" minor="1" major="0" misc10="0" miscgame="0" miscgross="0" match="0"></penalty>
<misc plus="1" minus="3" plusminus="-2" facewon="0" facelost="3" facepct=".000" blk="4"></misc>
</stats>
<stats year="TOTAL" gp="61">
<shots g="4" a="6" pts="10" sh="75" pct=".053" ps="0" psatt="0"></shots>
<goaltype gw="1" pp="0" sh="0" ua="1" fg="0" ot="0" en="0" hat="0" gt="0" so="0"></goaltype>
<penalty count="12" minutes="35" minor="10" major="1" misc10="0" miscgame="1" miscgross="0" match="0"></penalty>
<misc plus="8" minus="22" plusminus="-14" facewon="4" facelost="13" facepct=".235" blk="43"></misc>
</stats>
</player>
<player name="Player D" checkname="D,PLAYER">
<stats year="2018-19" gp="29">
<shots g="1" a="9" pts="10" sh="33" pct=".030" ps="0" psatt="0"></shots>
<goaltype gw="0" pp="1" sh="0" ua="0" fg="1" ot="0" en="0" hat="0" gt="0" so="0"></goaltype>
<penalty count="2" minutes="4" minor="2" major="0" misc10="0" miscgame="0" miscgross="0" match="0"></penalty>
<misc plus="12" minus="12" plusminus="0" facewon="116" facelost="94" facepct=".552" blk="8"></misc>
</stats>
<stats year="2019-20" gp="3">
<shots g="0" a="0" pts="0" sh="2" pct=".000" ps="0" psatt="0"></shots>
<goaltype gw="0" pp="0" sh="0" ua="0" fg="0" ot="0" en="0" hat="0" gt="0" so="0"></goaltype>
<penalty count="1" minutes="2" minor="1" major="0" misc10="0" miscgame="0" miscgross="0" match="0"></penalty>
<misc plus="0" minus="2" plusminus="-2" facewon="22" facelost="24" facepct=".478" blk="2"></misc>
</stats>
<stats year="TOTAL" gp="32">
<shots g="1" a="9" pts="10" sh="35" pct=".029" ps="0" psatt="0"></shots>
<goaltype gw="0" pp="1" sh="0" ua="0" fg="1" ot="0" en="0" hat="0" gt="0" so="0"></goaltype>
<penalty count="3" minutes="6" minor="3" major="0" misc10="0" miscgame="0" miscgross="0" match="0"></penalty>
<misc plus="12" minus="14" plusminus="-2" facewon="138" facelost="118" facepct=".539" blk="10"></misc>
</stats>
</player>
What I'm trying to do is have an xslt file, return the value and the name of the top player(s) for a specified stat category for a given season. For instance starting off with points (@pts).
I've been able to get it to get it to return the top result and player using some basic sorting, however, I want to add in more logic in situations where there are ties for the top.
This is what I have so far for finding the top points leader:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" version="1.0" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<STATS>
<season_leaders>
<xsl:for-each select="player/stats[@year='2019-20']">
<xsl:sort select="shots/@pts" data-type="number" order="descending"/>
<xsl:if test="(position() = 1)">
<points>
<xsl:value-of select="shots/@pts"/> - <xsl:value-of select="../@name"/>
</points>
</xsl:if>
</xsl:for-each>
</season_leaders>
</STATS>
</xsl:template>
</xsl:stylesheet>
Just a text return of the leading value and the leader's name, seperated by a dash, is exactly what I want. However, I would like it to compare the top three values, and in the case of a two-person tie, return the single top value and the last names of the two people tied. And in the case of a three-or-more-person tie, return the top value with text saying "multiple tied."
I've tried using the "position()" function in calling the value but it doesnt seem to work, i.e. <xsl:value-of select="shots[position()=1]/@pts"/>
and different variations.
Any help in how I can get it to compare the top sorted values and return different results based on the results of that comparison? Lastly, I believe it has to be done in XSLT version 1.0 sadly.
Try something like:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="year" select="'2019-20'"/>
<xsl:key name="stats" match="stats" use="concat(@year, '|', shots/@pts)" />
<xsl:template match="/players">
<STATS>
<season_leaders>
<xsl:variable name="max">
<xsl:for-each select="player/stats[@year=$year]/shots">
<xsl:sort select="@pts" data-type="number" order="descending"/>
<xsl:if test="(position()=1)">
<xsl:value-of select="@pts"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="leaders" select="key('stats', concat($year, '|', $max))" />
<points>
<xsl:value-of select="$max"/>
<xsl:text> - </xsl:text>
<xsl:choose>
<xsl:when test="count($leaders) > 2">multiple tied.</xsl:when>
<xsl:otherwise>
<xsl:for-each select="$leaders">
<xsl:value-of select="../@name"/>
<xsl:if test="position()!=last()">, </xsl:if>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</points>
</season_leaders>
</STATS>
</xsl:template>
</xsl:stylesheet>
Demo (using a well-formed XML as the input: http://xsltfiddle.liberty-development.net/gVrvcxm
sorry about that, the root is super basic: ` <hkcareer source="TAS For Ice Hockey" version="1.1.0" date="Oct 12, 2019"> <team code="" name=""></team> <player name="Player A" etc etc etc... </player> </hkcareer> ` hkcareer is the root node, and I had that in my xslt, but took it out of my example since it didnt have it in the example xml chunk.
Thank you so much for the help. I think this will work perfectly!
Additional question. Is there a way to add in a check so that players need to have a certain minimum of the team's total to qualify for the "leader". For instance, misc/facepct is a percentage of facewon and facelost, however if someone is only 1-for-1 they will be a leader with 1.00, but it doesnt reflect the true leader who may have over 100 total (won + lost). Is there a way to total all of the facewon and facelost for a given year, and check then if the leader has a specific % minimum of the totals, and if not, is skipped to the next leader and so on?
Please don't ask questions in comments. If necessary, post a new question with your new requirement. It sounds like a serious complication, but I am not sure I fully understand it.