'How to group two siblings with consecutive days in XSLT

I have a requirement to group consecutive days into 1 for the matching ID and am not able to figure out the solution, can someone please guide me on how to approach it.

Here are my input and expected output.

<?xml version='1.0' encoding='UTF-8'?>
<Member>
   <Data>
       <Id>X0001</Id>
       <Date>2022-01-01</Date>
   </Data> 
   <Data>
       <Id>X0001</Id>
       <Date>2022-01-02</Date>
   </Data> 
   <Data>
       <Id>X0001</Id>
       <Date>2022-01-03</Date>
   </Data> 
   <Data>
       <Id>X0001</Id>
       <Date>2022-01-04</Date>
   </Data> 
   <Data>
       <Id>X0001</Id>
       <Date>2022-01-05</Date>
   </Data> 
   <Data>
       <Id>X0001</Id>
       <Date>2022-01-08</Date>
   </Data> 
   <Data>
       <Id>X0001</Id>
       <Date>2022-01-12</Date>
   </Data> 
   <Data>
       <Id>X0004</Id>
       <Date>2022-01-09</Date>
   </Data> 
   <Data>
       <Id>X0005</Id>
       <Date>2022-01-10</Date>
   </Data> 
   <Data>
       <Id>X0005</Id>
       <Date>2022-01-11</Date>
   </Data> 
</Member>

    OUTPUT
----

Out>
  <Member>
    <Id>X0001</Id
    <SDate>2022-01-01</SDate>
    <EDate>2022-01-02</EDate
  </Member>
  <Member>
    <Id>X0001</Id
    <SDate>2022-01-03</SDate>
    <EDate>2022-01-04</EDate
  </Member>
  <Member>
    <Id>X0001</Id
    <SDate>2022-01-05</SDate>
    <EDate>2022-01-05</EDate
  </Member>
  <Member>
    <Id>X0001</Id
    <SDate>2022-01-08</SDate>
    <EDate>2022-01-08</EDate
  </Member>
  <Member>
    <Id>X0001</Id
    <SDate>2022-01-12</SDate>
    <EDate>2022-01-12</EDate
  </Member>
  <Member>
    <Id>X0004</Id
    <SDate>2022-01-09</SDate>
    <EDate>2022-01-09</EDate
  </Member>
  <Member>
    <Id>X0005</Id
    <SDate>2022-01-10</SDate>
    <EDate>2022-01-11</EDate
  </Member>
</Out>

I need to group if days are consecutive for each ID, if there is no following day then day should be left as is.



Solution 1:[1]

Here is one way you could look at it, I think:

XSLT 2.0

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/Member">
    <Out>
        <!-- group by id -->
        <xsl:for-each-group select="Data" group-by="Id">
            <!-- group contiguous ranges -->
            <xsl:for-each-group select="current-group()" group-starting-with="Data[not(xs:date(Date) = xs:date(preceding-sibling::Data[1]/Date) + xs:dayTimeDuration('P1D'))]">
                <!-- divide into pairs -->
                <xsl:for-each-group select="current-group()" group-by="(position()-1) idiv 2">
                    <Member>
                        <xsl:copy-of select="Id"/>
                        <SDate>
                            <xsl:value-of select="Date"/>
                        </SDate>
                        <EDate>
                            <xsl:value-of select="current-group()[last()]/Date"/>
                        </EDate>
                    </Member>
                </xsl:for-each-group>   
            </xsl:for-each-group>
        </xsl:for-each-group>
    </Out>
</xsl:template>

</xsl:stylesheet>

Note that this assumes that within each Id records are already sorted chronologically.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 michael.hor257k