issue with xsl-key for multiple elements

43 views Asked by At

I am trying to display the Hoursofoperation for different departments,i have a template that is working for single HoursofOperation element for multiple HoursofOperation elements it is not giving expected result. code is below

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

    <xsl:key name="similarDays" 
             match="DaysOfWeek" 
             use="concat(AvailabilityStartTimeMeasure, '|', AvailabilityEndTimeMeasure)" />

    <xsl:template match="/">

        <xsl:for-each select="dealers/HoursOfOperation">
            <xsl:if test="HoursTypeCode ='Service'">
                From service:
                    <xsl:for-each select="DaysOfWeek[count(. | key('similarDays', concat(AvailabilityStartTimeMeasure, '|', AvailabilityEndTimeMeasure))[1]) = 1]">

                        <xsl:for-each select="key('similarDays', concat(AvailabilityStartTimeMeasure, '|', AvailabilityEndTimeMeasure))">
                            <dt>
                                <xsl:value-of select="substring(DayOfWeekCode,1,3)"/>
                                <xsl:if test="position()!=last()">
                                    <xsl:text>, </xsl:text>
                                </xsl:if>
                            </dt>
                        </xsl:for-each>                                                 
                        <dd>
                            <xsl:call-template name="minutes2time">
                                <xsl:with-param name="minutes" select="AvailabilityStartTimeMeasure"/>
                            </xsl:call-template>
                            <xsl:text> - </xsl:text>
                            <xsl:call-template name="minutes2time">
                                <xsl:with-param name="minutes" select="AvailabilityEndTimeMeasure"/>
                            </xsl:call-template>
                        </dd>                   
                    </xsl:for-each>
            </xsl:if>
            <xsl:if test="HoursTypeCode ='Sales'">
                From Sales:
                <xsl:for-each select="DaysOfWeek[count(. | key('similarDays', concat(AvailabilityStartTimeMeasure, '|', AvailabilityEndTimeMeasure))[1]) = 1]">

                        <xsl:for-each select="key('similarDays', concat(AvailabilityStartTimeMeasure, '|', AvailabilityEndTimeMeasure))">
                            <dt>
                                <xsl:value-of select="substring(DayOfWeekCode,1,3)"/>
                                <xsl:if test="position()!=last()">
                                    <xsl:text>, </xsl:text>
                                </xsl:if>
                            </dt>
                        </xsl:for-each>                                                 
                        <dd>
                            <xsl:call-template name="minutes2time">
                                <xsl:with-param name="minutes" select="AvailabilityStartTimeMeasure"/>
                            </xsl:call-template>
                            <xsl:text>am - </xsl:text>
                            <xsl:call-template name="minutes2time">
                                <xsl:with-param name="minutes" select="AvailabilityEndTimeMeasure"/>
                            </xsl:call-template>
                            <xsl:text>pm</xsl:text>
                        </dd>

                </xsl:for-each>
            </xsl:if>
        </xsl:for-each>
    </xsl:template>

    <xsl:template name="minutes2time">
        <xsl:param name="minutes"/>
        <xsl:variable name="h" select="floor($minutes div 60)"/>    
        <xsl:variable name="m" select="$minutes mod 60"/>
        <xsl:variable name="pad" select="substring('0', 1, $m &lt; 10)"/>
        <xsl:choose>
            <xsl:when test="$h &gt; 12">
                <xsl:value-of select="concat(($h)-12, ':', $pad, $m)"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="concat($h, ':', $pad, $m)"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>    
</xsl:stylesheet>

XML:-

<dealers>
    <HoursOfOperation>
        <HoursTypeCode>Service</HoursTypeCode>
        <DaysOfWeek>
            <DayOfWeekCode>Monday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">420</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">1080</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
        <DaysOfWeek>
            <DayOfWeekCode>Tuesday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">420</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">1080</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
        <DaysOfWeek>
            <DayOfWeekCode>Wednesday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">420</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">1080</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
        <DaysOfWeek>
            <DayOfWeekCode>Thursday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">420</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">1080</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
        <DaysOfWeek>
            <DayOfWeekCode>Friday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">420</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">1080</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
        <DaysOfWeek>
            <DayOfWeekCode>Saturday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">420</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">1080</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
        <DaysOfWeek>
            <DayOfWeekCode>Sunday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">420</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">1080</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
    </HoursOfOperation>
    <HoursOfOperation>
        <HoursTypeCode>Parts</HoursTypeCode>
        <DaysOfWeek>
            <DayOfWeekCode>Monday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">480</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">1050</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
        <DaysOfWeek>
            <DayOfWeekCode>Tuesday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">480</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">1050</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
        <DaysOfWeek>
            <DayOfWeekCode>Wednesday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">480</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">1050</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
        <DaysOfWeek>
            <DayOfWeekCode>Thursday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">480</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">1050</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
        <DaysOfWeek>
            <DayOfWeekCode>Friday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">480</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">1050</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
        <DaysOfWeek>
            <DayOfWeekCode>Saturday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">480</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">990</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
        <DaysOfWeek>
            <DayOfWeekCode>Sunday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">420</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">1080</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
    </HoursOfOperation>
    <HoursOfOperation>
        <HoursTypeCode>Sales</HoursTypeCode>
        <DaysOfWeek>
            <DayOfWeekCode>Monday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">510</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">1140</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
        <DaysOfWeek>
            <DayOfWeekCode>Tuesday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">510</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">1140</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
        <DaysOfWeek>
            <DayOfWeekCode>Wednesday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">510</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">1140</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
        <DaysOfWeek>
            <DayOfWeekCode>Thursday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">510</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">1140</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
        <DaysOfWeek>
            <DayOfWeekCode>Friday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">510</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">1140</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
        <DaysOfWeek>
            <DayOfWeekCode>Saturday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">600</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">1080</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
        <DaysOfWeek>
            <DayOfWeekCode>Sunday</DayOfWeekCode>
            <AvailabilityStartTimeMeasure unitCode="minute">660</AvailabilityStartTimeMeasure>
            <AvailabilityEndTimeMeasure unitCode="minute">1080</AvailabilityEndTimeMeasure>
        </DaysOfWeek>
    </HoursOfOperation>
</dealers>

Output:-

From service:Mon, Tue, Wed, Thu, Fri, Sat, Sun, Sun7:00 - 6:00 From Sales:Mon, Tue, Wed, Thu, Fri8:30am - 7:00pmSat10:00am - 6:00pmSun11:00am - 6:00pm

template is working fine for Sales HoursOfOperation element but for service it is not working as expected.i didn't understand what this select statement is doing exactly <xsl:for-each select="DaysOfWeek[count(. | key('similarDays', concat(AvailabilityStartTimeMeasure, '|', AvailabilityEndTimeMeasure))[1]) = 1]">

Desired output:-

From service:Mon - Sun 7:00am - 6:00pm
From Sales:Mon - Fri 8:30am - 7:00pm
Sat 10:00am - 6:00pm
Sun 11:00am - 6:00pm
1

There are 1 answers

0
Ian Roberts On
DaysOfWeek[count(. | key('similarDays', concat(AvailabilityStartTimeMeasure, '|', AvailabilityEndTimeMeasure))[1]) = 1]

is an instance of the "Muenchian grouping" method, a way to group elements together based on some value that they have in common. This particular expression will select all the DaysOfWeek children of the current context element which are the first mention of their particular combination of start and end times within the whole document.

If you want to use Muenchian grouping but limit it to group within a particular section of the document then you need to include something that uniquely identifies that section as part of the key value (so that the first element with start time 1 and end time 2 within section A has a different key value from the first element with the same start and end time but within section B). For your case, a suitable unique identifier might be the HoursTypeCode:

<xsl:key name="similarDays" 
         match="DaysOfWeek" 
         use="concat(../HoursTypeCode, '|',
                     AvailabilityStartTimeMeasure, '|',
                     AvailabilityEndTimeMeasure)" />

The grouping expression needs to include the same unique identifier

<xsl:if test="HoursTypeCode ='Service'">
  From service:
  <xsl:for-each select="DaysOfWeek[count(. |
      key('similarDays', concat('Service|', AvailabilityStartTimeMeasure, '|', AvailabilityEndTimeMeasure))[1]) = 1]">

     <xsl:for-each select="key('similarDays', concat('Service|', AvailabilityStartTimeMeasure, '|', AvailabilityEndTimeMeasure))">

Of course, rather than the <xsl:if> blocks duplicating the whole set of logic for "from sales" and "from service" you could handle them both in the same block taking the section name from HoursTypeCode:

    <xsl:for-each select="dealers/HoursOfOperation">
      From <xsl:value-of select="HoursTypeCode"/>:
      <xsl:for-each select="DaysOfWeek[count(. |
          key('similarDays', concat(current()/HoursTypeCode, '|', AvailabilityStartTimeMeasure, '|', AvailabilityEndTimeMeasure))[1]) = 1]">
        <!-- etc. -->