Pozhil2013
Pozhil2013

Reputation: 1

How to display latest date from N number of month in xslt?

I need to display latest date in N number of months using xslt.

My input:

2016/10/18
2016//10/15
2016/09/29
2016/09/15

and so on.

My output should be like below:

2016/10/18
2016/09/29

Can anyone help me on this?

Upvotes: 0

Views: 459

Answers (2)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243579

I. Here is a short XSLT 2.0 solution:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

  <xsl:template match="/*">
    <xsl:for-each-group select="d" group-by="substring(.,6,2)">
      <xsl:sequence select="current-group()[. eq max(current-group()/string())][1]"/>
    </xsl:for-each-group>
  </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the following XML document (unordered and multi-year dates -- to make it more interesting):

<t>
    <d>2016/10/15</d>
    <d>2016/09/15</d>
    <d>2016/10/18</d>
    <d>2016/09/29</d>
    <d>2017/09/17</d>
</t>

the wanted, correct result is produced:

<d>2016/10/18</d>
<d>2017/09/17</d>

II. If the date that has the same month's highest day is wanted -- regardless of the year, this transformation:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

  <xsl:template match="/*">
    <xsl:for-each-group select="d" group-by="substring(.,6,2)">
      <xsl:sequence select=
          "current-group()[substring(.,9,2) eq max(current-group()/substring(.,9,2))][1]"/>
    </xsl:for-each-group>
  </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the same XML document (above), the correct result is produced:

<d>2016/10/18</d>
<d>2016/09/29</d>

III. If the dates are given together as a string:

Just use the tokenize() standard XPath 2.0 fy=unction.

For example, the equivalent of the first transformation above becomes:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:variable name="vDates" 
               select="'2016/10/15 2016/09/15 2016/10/18 2016/09/29 2017/09/17'"/>


  <xsl:template match="/">
    <xsl:for-each-group select="tokenize($vDates, '\s+')[.]" group-by="substring(.,6,2)">
      <xsl:sequence select="max(current-group())"/>      
    </xsl:for-each-group>
  </xsl:template>
</xsl:stylesheet>

Upvotes: 0

Martin Honnen
Martin Honnen

Reputation: 167716

Given a string of dates in that format you first need to tokenize to extract the date values, then you need to convert to the xs:date format, then you can group by the month and select the maximum value in each group. Using XSLT 3.0 that can be done as follows:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    exclude-result-prefixes="xs math"
    version="3.0">

    <xsl:param name="input" as="xs:string">2016/10/18 2016/10/15 2016/09/29 2016/09/15</xsl:param>
    <xsl:variable name="dates" as="xs:date*"
        select="tokenize($input, '\s+')!xs:date(replace(., '/', '-'))"/>

    <xsl:variable name="max-dates" as="xs:date*">
        <xsl:for-each-group select="$dates" group-by="month-from-date(.)">
            <xsl:sort select="current-grouping-key()"/>
            <xsl:sequence select="max(current-group())"/>
        </xsl:for-each-group>
    </xsl:variable>

    <xsl:template name="main" match="/">
        <xsl:value-of select="$max-dates" separator="&#10;"/>
    </xsl:template>

</xsl:stylesheet>

In XSLT 2.0 you need to rewrite the date sequence construction a bit:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    exclude-result-prefixes="xs math"
    version="2.0">

    <xsl:param name="input" as="xs:string">2016/10/18 2016/10/15 2016/09/29 2016/09/15</xsl:param>
    <xsl:variable name="dates" as="xs:date*"
        select="for $dateString in tokenize($input, '\s+') return xs:date(replace($dateString, '/', '-'))"/>

    <xsl:variable name="max-dates" as="xs:date*">
        <xsl:for-each-group select="$dates" group-by="month-from-date(.)">
            <xsl:sort select="current-grouping-key()"/>
            <xsl:sequence select="max(current-group())"/>
        </xsl:for-each-group>
    </xsl:variable>

    <xsl:template name="main" match="/">
        <xsl:value-of select="$max-dates" separator="&#10;"/>
    </xsl:template>

</xsl:stylesheet>

Upvotes: 0

Related Questions