Find the maximum value of all children and get its parent in XSLT

Using the XML below, I need to figure out which person worked more hours on each site. For example, in the XML below, person 1 worked 8 hours on site 1, but person 2 worked only 6 hours. Thus, the result should contain person 1 and site 1 in the converted XML. If the clock is equal, select the first person.

EDIT: I want this to be implemented using XSLT 1.0.

<root> <WorkSite Person="P1" Site="S1"> <Hours>8</Hours> </WorkSite> <WorkSite Person="P1" Site="S2"> <Hours>2</Hours> </WorkSite> <WorkSite Person="P1" Site="S3"> <Hours>9</Hours> </WorkSite> <WorkSite Person="P2" Site="S1"> <Hours>6</Hours> </WorkSite> <WorkSite Person="P2" Site="S2"> <Hours>10</Hours> </WorkSite> <WorkSite Person="P2" Site="S3"> <Hours>2</Hours> </WorkSite> </root> 

The result of the XSLT conversion should be like this:

 <root> <WorkSite Person="P1" Site="S1"/> <WorkSite Person="P2" Site="S2"/> <WorkSite Person="P1" Site="S3"/> </root> 
+4
source share
3 answers

Convert XSLT 1.0 :

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:key name="kSiteByName" match="@Site" use="."/> <xsl:key name="kWorksiteBySite" match="WorkSite" use="@Site"/> <xsl:variable name="vSites" select= "/*/*/@Site[generate-id() = generate-id(key('kSiteByName',.)[1]) ]" /> <xsl:template match="/"> <root> <xsl:for-each select="$vSites"> <xsl:for-each select="key('kWorksiteBySite', .)"> <xsl:sort select="Hours" data-type="number" order="descending"/> <xsl:if test="position()=1"> <xsl:copy> <xsl:copy-of select="@*"/> </xsl:copy> </xsl:if> </xsl:for-each> </xsl:for-each> </root> </xsl:template> </xsl:stylesheet> 

when applied to the provided XML document :

 <root> <WorkSite Person="P1" Site="S1"> <Hours>8</Hours> </WorkSite> <WorkSite Person="P1" Site="S2"> <Hours>2</Hours> </WorkSite> <WorkSite Person="P1" Site="S3"> <Hours>9</Hours> </WorkSite> <WorkSite Person="P2" Site="S1"> <Hours>6</Hours> </WorkSite> <WorkSite Person="P2" Site="S2"> <Hours>10</Hours> </WorkSite> <WorkSite Person="P2" Site="S3"> <Hours>2</Hours> </WorkSite> </root> 

creates the desired, correct result :

 <root> <WorkSite Person="P1" Site="S1"/> <WorkSite Person="P2" Site="S2"/> <WorkSite Person="P1" Site="S3"/> </root> 

Please note :

  • Using the Muenchian method to group to find all the different Site values.

  • The maximum path was found sorting in descending order and getting the first result from the sorted node -list.

+7
source
 <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <root> <xsl:for-each-group select="*/WorkSite" group-by="@Site"> <WorkSite Person="{(current-group()[Hours = max(current-group()/Hours)])[1]/@Person}" Site="{current-grouping-key()}" /> </xsl:for-each-group> </root> </xsl:template> </xsl:stylesheet> 
+2
source

XSLT 1.0 Solution. This style sheet:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:key name="BySite" match="WorkSite" use="@Site"/> <xsl:template match="root"> <root> <xsl:for-each select="/*/WorkSite[count(.|key('BySite',@Site)[1])=1]"> <WorkSite Person="{key('BySite',@Site) [not(key('BySite',@Site)/Hours > Hours)]/@Person}" Site="{@Site}" /> </xsl:for-each> </root> </xsl:template> </xsl:stylesheet> 

Conclusion:

 <root> <WorkSite Person="P1" Site="S1" /> <WorkSite Person="P2" Site="S2" /> <WorkSite Person="P1" Site="S3" /> </root> 
0
source

All Articles