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('&#xa;',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('&#xa;',$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:

  1. in variable $vhalfhrs have elements values starting time of every half-hour period during day.

  2. in template matches every row, $vstart variable set ti half-hour start-time, in time of current node (row) falls into.

  3. the variable $vprecrow set preceding sibling (row) of current row.

  4. if time of preceding row not later start-half-hour-time (in $vstart), currentrow` first 1 in half-hour period.

  5. we output starting half-hour period time.

  6. we calculate , output sum of row elements time in same half-hour time period. following siblings of current row , 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=    "'&#xa;',      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:

  1. we using <xsl:for-each-group> group-adjacent attribute set expression calculating position of 1/2 hour period in row/time is.

  2. heavy use of standard functions current-group() , current-grouping-key() .


Comments

Popular posts from this blog

apache - Add omitted ? to URLs -

redirect - bbPress Forum - rewrite to wwww.mysite prohibits login -

php - How can I stop spam on my custom forum/blog? -