xsltgroup-byforeachxslt-2.0xslt-grouping

XSLT for-each with using last and aggregation


I am not sure if this is possible with XSLT but I am trying to get the below XML into a format where it is name, title, date (if same date then only get date once), last value of In time (might not be present), last value of Out time (might not be present), and the sum of the hours for that date. I have tried grouping based on ID and then another grouping based on Details/Date but that seems to not be grouping like dates together.

Sample output would be:

Bill,President,2025-07-01-07:00,2025-07-01T08:00:00-04:00,2025-07-01T12:00:00-04:00,4
Scott,Vice President,2025-07-03-07:00,,8
Scott,Vice President,2025-07-07-07:00,,8
Scott,Vice President,2025-07-08-07:00,,8 

Sample XML:

<Data>    
    <Entry>
        <ID>1</ID>
        <Name>Bill</Name>
        <Title>President</Title>
        <Details>
            <Date>2025-07-01-07:00</Date>
            <In_Time>2025-07-01T08:00:00-04:00</In_Time>
            <Out_Time>2025-07-01T12:00:00-04:00</Out_Time>
            <Hours>4</Hours>
            <Type>PTO</Type>
        </Details>
        <Details>
            <Date>2025-07-01-07:00</Date>
            <In_Time>2025-07-01T08:00:00-04:00</In_Time>
            <Out_Time>2025-07-01T15:00:00-04:00</Out_Time>
            <Hours>2</Hours>
            <Type>PTO</Type>
        </Details>
        <Details>
            <Date>2025-07-01-07:00</Date>
            <In_Time>2025-07-01T08:00:00-04:00</In_Time>
            <Out_Time>2025-07-01T12:00:00-04:00</Out_Time>
            <Hours>-2</Hours>
            <Type>PTO</Type>
        </Details>
    </Entry>
    <Entry>
        <ID>2</ID>
        <Name>Scott</Name>
        <Title>Vice President</Title>
        <Details>
            <Date>2025-07-03-07:00</Date>
            <Hours>8</Hours>
            <Type>Holiday</Type>
        </Details>
        <Details>
            <Date>2025-07-07-07:00</Date>
            <Hours>8</Hours>
            <Type>Holiday</Type>
        </Details>
        <Details>
            <Date>2025-07-08-07:00</Date>
            <Hours>8</Hours>
            <Type>PTO</Type>
        </Details>
    </Entry>
</Data>

Solution

  • If you use the concatenation of the Name, Title, and Date as your @group-by key in xsl:for-each-group:

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
      <xsl:output method="text"/>
      <xsl:template match="/">
        <xsl:for-each-group select="/Data/Entry/Details" group-by="string-join((../Name, ../Title, Date), ',')">
          <xsl:variable name="in" select="head((current-group()[last()]/In_Time, ''))"/>
          <xsl:variable name="out" select="head((current-group()[last()]/Out_Time, ''))"/>
                
          <xsl:value-of select="../Name, ../Title, Date, $in, $out, sum(current-group()/Hours)" 
            separator=","/>
          <xsl:text>&#10;</xsl:text>
        </xsl:for-each-group>
      </xsl:template>
    </xsl:stylesheet>