Re: [xsl] A challenge.. Group Periods of Data (1..5, 2..8, 4..9) (10..12; 10..14)

Subject: Re: [xsl] A challenge.. Group Periods of Data (1..5, 2..8, 4..9) (10..12; 10..14)
From: Dimitre Novatchev <dnovatchev@xxxxxxxxx>
Date: Wed, 4 May 2005 07:29:31 +1000
On 5/4/05, Karl Stubsjoen <kstubs@xxxxxxxxx> wrote:
> A challenge, group the following XML into 2 periods.  The periods are
> arbitrary, but for this example they happen to be:
> Period 1:  1 - 12
> Period 2:  14 - 30
>
> Expected Result:
> <result>
>  <period begins="1" ends="12">
>    <B period_begin="1" period_end="5"/>
>    <B period_begin="2" period_end="7"/>
>    <B period_begin="3" period_end="10"/>
>    <B period_begin="4" period_end="12"/>
>  </period>
>  <period begins="14" ends="30">
>    <B period_begin="14" period_end="16"/>
>    <B period_begin="16" period_end="20"/>
>    <B period_begin="16" period_end="30"/>
>  </period>
> </result>
>
> Source XML / Result (sorted)
> <A>
>  <B period_begin="1" period_end="5"/>
>  <B period_begin="2" period_end="7"/>
>  <B period_begin="3" period_end="10"/>
>  <B period_begin="4" period_end="12"/>
>  <B period_begin="14" period_end="16"/>
>  <B period_begin="16" period_end="20"/>
>  <B period_begin="16" period_end="30"/>
> </A>
>
> Source XML / Result (un-sorted)
> <A>
>  <B period_begin="14" period_end="16"/>
>  <B period_begin="2" period_end="7"/>
>  <B period_begin="16" period_end="20"/>
>  <B period_begin="1" period_end="5"/>
>  <B period_begin="4" period_end="12"/>
>  <B period_begin="16" period_end="30"/>
>  <B period_begin="3" period_end="10"/>
> </A>


Hi Karl,

This has an elegant solution using the f:foldl() function of FXSL.

Here, I'm giving a "first glance" XSLT 2.0 solution without using FXSL.

This transformation:

<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 omit-xml-declaration="yes" indent="yes"/>

  <xsl:template match="A">
    <xsl:variable name="vStarting" select=
    "*[not(@period_begin/xs:integer(.)
         &lt;=
           preceding-sibling::*/@period_end/xs:integer(.)
           )]">
    </xsl:variable>

    <xsl:for-each select="$vStarting">
      <xsl:variable name="vPos" select="position()"/>
      <period start="{@period_begin}"
           end="{if ($vPos = last() )
                 then
                    max( (. | following-sibling::*)
                           /@period_end/xs:integer(.)
                        )
                 else
                    max( (. | following-sibling::*)
	                       [. &lt;&lt; $vStarting[$vPos + 1]]
		                           /@period_end/xs:integer(.)
                     )
                 }"
       />
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>


when applied on this source xml document (added one more group to yours):

<A>
 <B period_begin="1" period_end="5"/>
 <B period_begin="2" period_end="7"/>
 <B period_begin="3" period_end="10"/>
 <B period_begin="4" period_end="12"/>
 <B period_begin="14" period_end="16"/>
 <B period_begin="16" period_end="20"/>
 <B period_begin="16" period_end="30"/>
 <B period_begin="32" period_end="33"/>
 <B period_begin="33" period_end="38"/>
</A>

produces the wanted result:

<period start="1" end="12"/>
<period start="14" end="30"/>
<period start="32" end="38"/>


Cheers,
Dimitre Novatchev

Current Thread