user14512303
user14512303

Reputation:

XSLT merge rows if they have the same value

I'm fairly new to XML and XSLT documents. I have created an XML document that has records of weather data which spans over a few months. I want to use an XSLT document to group together records of the same month within each table row and then print their months (if month = 2, Feb will be printed) on the column which spans across all the rows in the same month. Is there any way I can use if-else statements to print out the corresponding months and how do I merge the table rows if they have the same month value?

XML:

<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="S3(1).xsl" ?>

<forecast xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
            xsi:noNamespaceSchemaLocation="S3.xsd"
            qTime="28/10/20 10:00 PM">
            
  <weather yyyymmdd="20200430">
    <year>2020</year>   
    <month>04</month>
    <date>30</date>
    <comment>Plenty of sunshine</comment>
    <code>sunny</code>
    <highest>32.6</highest>
    <lowest>28.4</lowest>
  </weather>
  <weather yyyymmdd="20200218">
    <year>2020</year>   
    <month>02</month>
    <date>18</date>
    <comment>Plenty of sunshine</comment>
    <code>sunny</code>
    <highest>34.6</highest>
    <lowest>30.5</lowest>
  </weather>
  <weather yyyymmdd="20200710">
    <year>2020</year>   
    <month>07</month>
    <date>10</date>
    <comment>Partly sunny</comment>
    <code>partlySunny</code>
    <highest>33.1</highest>
    <lowest>29.2</lowest>
  </weather>
  <weather yyyymmdd="20200616">
    <year>2020</year>   
    <month>06</month>
    <date>16</date>
    <comment>Considerable clouds</comment>
    <code>cloudy</code>
    <highest>30.5</highest>
    <lowest>25.4</lowest>
  </weather>
  <weather yyyymmdd="20200612">
    <year>2020</year>   
    <month>06</month>
    <date>12</date>
    <comment>Cloudy with a thunderstorm</comment>
    <code>thunderstorm</code>
    <highest>29.1</highest>
    <lowest>23.2</lowest>
  </weather>
  <weather yyyymmdd="20200421">
    <year>2020</year>   
    <month>04</month>
    <date>21</date>
    <comment>Plenty of sunshine</comment>
    <code>sunny</code>
    <highest>32.2</highest>
    <lowest>29.8</lowest>
  </weather>
  <weather yyyymmdd="20200628">
    <year>2020</year>   
    <month>06</month>
    <date>28</date>
    <comment>A morning shower, then rain</comment>
    <code>rain</code>
    <highest>30.2</highest>
    <lowest>22.7</lowest>
  </weather>
    <weather yyyymmdd="20200502">
    <year>2020</year>   
    <month>05</month>
    <date>02</date>
    <comment>Cloudy with a thunderstorm</comment>
    <code>thunderstorm</code>
    <highest>28.1</highest>
    <lowest>26.9</lowest>
  </weather>
  <weather yyyymmdd="20200428">
    <year>2020</year>   
    <month>04</month>
    <date>28</date>
    <comment>A morning shower</comment>
    <code>rain</code>
    <highest>28.8</highest>
    <lowest>22.2</lowest>
  </weather>
  <weather yyyymmdd="20200410">
    <year>2020</year>   
    <month>04</month>
    <date>10</date>
    <comment>Partly sunny</comment>
    <code>partlySunny</code>
    <highest>33.7</highest>
    <lowest>29.3</lowest>
  </weather>
  <weather yyyymmdd="20200730">
    <year>2020</year>   
    <month>07</month>
    <date>30</date>
    <comment>Plenty of sunshine</comment>
    <code>sunny</code>
    <highest>32.3</highest>
    <lowest>28.4</lowest>
  </weather>
  <weather yyyymmdd="20200706">
    <year>2020</year>   
    <month>07</month>
    <date>06</date>
    <comment>Plenty of sunshine</comment>
    <code>sunny</code>
    <highest>34.5</highest>
    <lowest>30.6</lowest>
  </weather>
</forecast>

This is my XSL file so far:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
<html>
    <body>
    <xsl:for-each select="forecast">
    <h1>
        <span> <xsl:value-of select="@qLocation"/></span>
        <span>[<xsl:value-of select="@qTime"/>]</span>
    </h1>
    
    <table style="border:1px solid black;">
    <tr>
          <th>Date</th>
          <th>Weather data</th>
          <th>Highest</th>
          <th>Lowest</th>
    </tr>
    
    <xsl:for-each select="weather"> 
        <xsl:sort select="month" order="ascending"/>
        <xsl:sort select="date" order="ascending"/>
            <tr>    
              <td>
                <span><xsl:value-of select="date"/>/</span>
                <span><xsl:value-of select="month"/></span>
            </td>
            <td>
              <li>
                <xsl:value-of select="date"/>/
                <xsl:value-of select="month"/>/
                <xsl:value-of select="year"/>,
                from
                <xsl:value-of select="lowest"/>C
                to
                <xsl:value-of select="highest"/>C,
                <xsl:value-of select="comment"/>

              </li>
            </td>
            <td>
              <xsl:value-of select="highest"/>C
              <!-- <img src="img1.jpg"> -->
            </td>
            <td>
              <xsl:value-of select="lowest"/>C
              <!-- <img src="img2.jpg"> -->
            </td>
            </tr>           
        </xsl:for-each>
    </table>
    </xsl:for-each>
    </body>
</html>
</xsl:template>
</xsl:stylesheet>

The output I'm trying to achieve is something like this:

Image

Upvotes: 4

Views: 735

Answers (1)

Mads Hansen
Mads Hansen

Reputation: 66783

For this, I would use xsl:for-each-group specifying the month value for the grouping-key: <xsl:for-each-group select="weather" group-by="month">

Inside of the loop, iterate over the current-group() to process each of the grouped weather elements to produce the list: <xsl:for-each select="current-group()">

You can obtain the min() and max() values from the grouped set of weather elements: max(current-group()/highest)

Using the month number as a position, you can use a predicate to select from a sequence of month abbreviations. There was a leading zero, so use number() to convert i.e. 02 to 2: <xsl:sequence select="('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec')[number(current-grouping-key())]"/>

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    <xsl:template match="/">
        <html>
            <body>
               <xsl:apply-templates/>
            </body>
        </html>
    </xsl:template>
    
    <xsl:template match="forecast">
        <h1>
            <span> <xsl:value-of select="@qLocation"/></span>
            <span>[<xsl:value-of select="@qTime"/>]</span>
        </h1>
        
        <table style="border:1px solid black;">
            <tr>
                <th>Date</th>
                <th>Weather data</th>
                <th>Highest</th>
                <th>Lowest</th>
            </tr>
            
            <xsl:for-each-group select="weather" group-by="month"> 
                <xsl:sort select="month" order="ascending"/>
                <xsl:sort select="date" order="ascending"/>
                <tr>    
                    <td>
                        <span><xsl:value-of select="year"/>/</span>
                        <span><xsl:sequence select="('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec')[number(current-grouping-key())]"/></span>
                    </td>
                    <td>
                        <xsl:for-each select="current-group()">
                            <li>
                                <xsl:value-of select="date"/>/
                                <xsl:value-of select="month"/>/
                                <xsl:value-of select="year"/>,
                                from
                                <xsl:value-of select="lowest"/>C
                                to
                                <xsl:value-of select="highest"/>C,
                                <xsl:value-of select="comment"/>
                                
                            </li>
                        </xsl:for-each>
                    </td>
                    <td>
                        <xsl:value-of select="max(current-group()/highest)"/>C
                        <!-- <img src="img1.jpg"> -->
                    </td>
                    <td>
                        <xsl:value-of select="min(current-group()/lowest)"/>C
                        <!-- <img src="img2.jpg"> -->
                    </td>
                </tr>           
            </xsl:for-each-group>
        </table>
    </xsl:template>
    
</xsl:stylesheet>

Upvotes: 1

Related Questions