Search code examples
xmlxsltxpathgroup-bymuenchian-grouping

XSLT - How to use group by in this situation?


(A follow up of this thread)

I now actually need to know the amount of visiters grouped by Day and then by period. I tried to apply the group-by suggested by Kirill Polishchuk, but I can only get it to group by one certain element. Now I actually need to know how to apply multiple group-by's? E.g. Group by 'Day' first and then by period.

I have the following XML:

<Cinema>
    <Day date="15-09-2011" day="Thursday" week="37" in="543" out="543">
        <Movie name="movie1" in="191" out="191">
            <Period time="16:00:00" in="20" out="20"/>
            <Period time="18:00:00" in="71" out="70"/>
            <Period time="20:00:00" in="100" out="101"/>
        </Movie>
        <Movie name="movie2" in="105" out="105">
            <Period time="16:00:00" in="13" out="13"/>
            <Period time="18:00:00" in="34" out="34"/>
            <Period time="20:00:00" in="58" out="58"/>
        </Movie>
        <Movie name="movie3" in="247" out="247">
            <Period time="16:00:00" in="57" out="57"/>
            <Period time="18:00:00" in="75" out="72"/>
            <Period time="20:00:00" in="115" out="118"/>
        </Movie>
    <Day>
    <Day date="16-09-2011" day="Friday" week="37" in="1151" out="1151">
        <Movie name="movie1" in="364" out="364">
            <Period time="16:00:00" in="106" out="106"/>
            <Period time="18:00:00" in="131" out="129"/>
            <Period time="20:00:00" in="127" out="129"/>
        </Movie>
        <Movie name="movie2" in="333" out="333">
            <Period time="16:00:00" in="89" out="89"/>
            <Period time="18:00:00" in="116" out="116"/>
            <Period time="20:00:00" in="128" out="128"/>
        </Movie>
        <Movie name="movie3" in="454" out="454">
            <Period time="16:00:00" in="104" out="104"/>
            <Period time="18:00:00" in="150" out="150"/>
            <Period time="20:00:00" in="200" out="200"/>
        </Movie>
    <Day>
</Cinema>

What I am trying to get is the total visitors of each movie period per day. For example:

Thursday:
16:00h - in: 90, out: 90
18:00h - in: 180, out: 176
20:00h - in: 273, out: 277
Total - in: 543, out: 543

Friday: 
16:00h - in: 299, out: 299
18:00h - in: 397, out: 395
20:00h - in: 455, out: 457
Total - in: 1151, out: 1151

Does anyone have any suggestions? Thanks in advance!

(I use xslt 1.0 at the moment, but it is possible for me to "upgrade" to the newer 2.0, if necessary)


Solution

  • <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
        <xsl:key name="k" match="Period" use="concat(../../@date, @time)"/>
    
        <xsl:template match="/Cinema">
            <xsl:apply-templates select="Day"/>
        </xsl:template>
    
        <xsl:template match="Day">
            <xsl:value-of select="concat(@day, ':')"/>
            <xsl:text>&#xA;</xsl:text>
    
            <xsl:apply-templates select="*/Period[generate-id(.) = generate-id(key('k', concat(../../@date, @time)))]"/>
    
            <xsl:value-of select="concat('Total - in: ', @in, ', out: ', @out)"/>
            <xsl:text>&#xA;&#xA;</xsl:text>
        </xsl:template>
    
        <xsl:template match="Period">
            <xsl:value-of select="
                          concat(substring(@time, 1, 5), 'h - in: ', 
                            sum(key('k', concat(../../@date, @time))/@in), 
                            ', out: ', 
                            sum(key('k', concat(../../@date, @time))/@out))"/>
            <xsl:text>&#xA;</xsl:text>
        </xsl:template>
    
    </xsl:stylesheet>
    

    XML:

    <Cinema>
        <Day date="15-09-2011" day="Thursday" week="37" in="543" out="543">
            <Movie name="movie1" in="191" out="191">
                <Period time="16:00:00" in="20" out="20"/>
                <Period time="18:00:00" in="71" out="70"/>
                <Period time="20:00:00" in="100" out="101"/>
            </Movie>
            <Movie name="movie2" in="105" out="105">
                <Period time="16:00:00" in="13" out="13"/>
                <Period time="18:00:00" in="34" out="34"/>
                <Period time="20:00:00" in="58" out="58"/>
            </Movie>
            <Movie name="movie3" in="247" out="247">
                <Period time="16:00:00" in="57" out="57"/>
                <Period time="18:00:00" in="75" out="72"/>
                <Period time="20:00:00" in="115" out="118"/>
            </Movie>
        </Day>
        <Day date="16-09-2011" day="Friday" week="37" in="1151" out="1151">
            <Movie name="movie1" in="364" out="364">
                <Period time="16:00:00" in="106" out="106"/>
                <Period time="18:00:00" in="131" out="129"/>
                <Period time="20:00:00" in="127" out="129"/>
            </Movie>
            <Movie name="movie2" in="333" out="333">
                <Period time="16:00:00" in="89" out="89"/>
                <Period time="18:00:00" in="116" out="116"/>
                <Period time="20:00:00" in="128" out="128"/>
            </Movie>
            <Movie name="movie3" in="454" out="454">
                <Period time="16:00:00" in="104" out="104"/>
                <Period time="18:00:00" in="150" out="150"/>
                <Period time="20:00:00" in="200" out="200"/>
            </Movie>
        </Day>
    </Cinema>
    

    Output:

    Thursday:
    16:00h - in: 90, out: 90
    18:00h - in: 180, out: 176
    20:00h - in: 273, out: 277
    Total - in: 543, out: 543
    
    Friday:
    16:00h - in: 299, out: 299
    18:00h - in: 397, out: 395
    20:00h - in: 455, out: 457
    Total - in: 1151, out: 1151