grouping - XSLT group by half hour -
given xml:
<root> <row> <time>08:00</time> <sales>800</sales> </row> <row> <time>08:15</time> <sales>815</sales> </row> <row> <time>08:30</time> <sales>830</sales> </row> <row> <time>08:45</time> <sales>845</sales> </row> <row> <time>11:00</time> <sales>1100</sales> </row> <row> <time>11:45</time> <sales>1145</sales> </row> <row> <time>14:15</time> <sales>1415</sales> </row> <row> <time>14:30</time> <sales>1430</sales> </row> </root>
i trying find way xslt transform summarizing sales 30 minute intervals. can summarize hourly intervals 60 minutes using muenchian method, cannot use 30 minute since need custom function (but cannot use xslt 2.0, nor .net's custom functions). please help!
the expected output this:
30 minute 08:00 $1600 08:30 $1675 11:00 $1100 11:30 $1145 14:00 $1415 14:30 $1430
solution 1.
this transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/xsl/transform"> <xsl:output method="text"/> <xsl:key name="krowsinhalfhour" match= "row[not((substring-after(time,':')+30) mod 30 = 0)]" use="generate-id( preceding-sibling::row [60*substring-before(time,':') + substring-after(time,':') >= 60*substring-before(current()/time,':') + substring-after(current()/time,':') - substring-after(current()/time,':') mod 30 ] [1] ) "/> <xsl:template match= "row[(substring-after(time,':')+30) mod 30 = 0 or not( 60*substring-before(preceding-sibling::row[1]/time,':') + substring-after(preceding-sibling::row[1]/time,':') >= 60*substring-before(time,':') + substring-after(time,':') - substring-after(time,':') mod 30 ) ] "> <xsl:variable name="vprevstartmins" select= "60*substring-before(time,':') + substring-after(time,':') - substring-after(time,':') mod 30 "/> <xsl:value-of select= "concat('
',floor($vprevstartmins div 60), ':', concat(substring('0',($vprevstartmins mod 60 >0)+1), $vprevstartmins mod 60 ) ) "/> <xsl:text> $</xsl:text> <xsl:value-of select= "sum(sales | key('krowsinhalfhour',generate-id())/sales)"/> </xsl:template> <xsl:template match="text()"/> </xsl:stylesheet>
when applied on provided xml document:
<root> <row> <time>08:00</time> <sales>800</sales> </row> <row> <time>08:15</time> <sales>815</sales> </row> <row> <time>08:30</time> <sales>830</sales> </row> <row> <time>08:45</time> <sales>845</sales> </row> <row> <time>11:00</time> <sales>1100</sales> </row> <row> <time>11:45</time> <sales>1145</sales> </row> <row> <time>14:15</time> <sales>1415</sales> </row> <row> <time>14:30</time> <sales>1430</sales> </row> </root>
produces wanted, correct result:
8:00 $1615 8:30 $1675 11:00 $1100 11:30 $1145 14:00 $1415 14:30 $1430
solution 2:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/xsl/transform" xmlns:my="my:my" > <xsl:output method="text"/> <xsl:strip-space elements="*"/> <my:halfhours> <t>00:00</t><t>00:30</t><t>01:00</t><t>01:30</t> <t>02:00</t><t>02:30</t><t>03:00</t><t>03:30</t> <t>04:00</t><t>04:30</t><t>05:00</t><t>05:30</t> <t>06:00</t><t>06:30</t><t>07:00</t><t>07:30</t> <t>08:00</t><t>08:30</t><t>09:00</t><t>09:30</t> <t>10:00</t><t>10:30</t><t>11:00</t><t>11:30</t> <t>12:00</t><t>12:30</t><t>13:00</t><t>13:30</t> <t>14:00</t><t>14:30</t><t>15:00</t><t>15:30</t> <t>16:00</t><t>16:30</t><t>17:00</t><t>17:30</t> <t>18:00</t><t>18:30</t><t>19:00</t><t>19:30</t> <t>20:00</t><t>20:30</t><t>21:00</t><t>21:30</t> <t>22:00</t><t>22:30</t><t>23:00</t><t>23:30</t> <t>24:00</t> </my:halfhours> <xsl:variable name="vhalfhrs" select= "document('')/*/my:halfhours/*"/> <xsl:template match="row"> <xsl:variable name="vstart" select= "$vhalfhrs[translate(.,':','') > translate(current()/time,':','') ][1] /preceding-sibling::*[1] "/> <xsl:variable name="vprecrow" select= "preceding-sibling::*[1]"/> <xsl:if test= "not(translate($vprecrow/time,':','') >= translate($vstart,':','') )"> <xsl:value-of select="concat('
',$vstart, ' $')"/> <xsl:value-of select= "sum(sales|following-sibling::* [not(translate(time,':','') >= translate($vstart/following-sibling::*,':','') ) ] /sales ) "/> </xsl:if> </xsl:template> </xsl:stylesheet>
when transformation applied on same xml document, again wanted, correct result produced:
08:00 $1615 08:30 $1675 11:00 $1100 11:30 $1145 14:00 $1415 14:30 $1430
explanation:
in variable
$vhalfhrs
have elements values starting time of every half-hour period during day.in template matches every
row
,$vstart
variable set ti half-hour start-time, intime
of current node (row
) falls into.the variable
$vprecrow
set preceding sibling (row
) of currentrow
.if time of preceding
row
not later start-half-hour-time (in $vstart), current
row` first 1 in half-hour period.we output starting half-hour period time.
we calculate , output sum of
row
elementstime
in same half-hour time period. following siblings of currentrow
,time
not greater or equal start of next half-hour period.
solution 3 (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"> <xsl:output method="text"/> <xsl:template match="/*"> <xsl:for-each-group select="row" group-adjacent= "(xs:integer(substring-before(time,':'))*60 + xs:integer(substring-after(time,':')) ) idiv 30 "> <xsl:variable name="vstartminutes" select="current-grouping-key()*30"/> <xsl:value-of separator="" select= "'
', format-number($vstartminutes idiv 60, '00'), ':', format-number($vstartminutes mod 60,'00'), ' $', sum(current-group()/sales/xs:integer(.)) "/> </xsl:for-each-group> </xsl:template> </xsl:stylesheet>
when transformation applied on same xml document above, same wanted, correct result produced:
08:00 $1615 08:30 $1675 11:00 $1100 11:30 $1145 14:00 $1415 14:30 $1430
explanation:
we using
<xsl:for-each-group>
group-adjacent
attribute set expression calculating position of 1/2 hour period inrow/time
is.heavy use of standard functions
current-group()
,current-grouping-key()
.
Comments
Post a Comment