tenwest
tenwest

Reputation: 2317

XSLT Grouping by date range, and counting grouped elements -- possible?

Two issues here. I have XML structured like the following:

<root>
    <Detection>
        <Time>2000-01-01T00:12:00Z</Time>
    </Detection>
    <Detection>
        <Time>2000-01-01T00:34:00Z</Time>
    </Detection>
    <Detection>
        <Time>2000-01-01T02:33:00Z</Time>
    </Detection>
</root>

I would like to transform this xml by matching elements based on a date range. Specifically,<Detection>s that occur in the same hour should be grouped together, and replaced with a single <Detection> specifying that hour, including a <Count> element which tells us how many original <Detection>s are in this new <Detection>. Confusing?

Example output based on above input:

<root>
    <Detection>
    <!-- there were two detections occuring during the midnight hour -->
        <Time>2000-01-01T00:00:00Z</Time>
        <Count>2</Count>
    </Detection>
    <Detection>
    <!-- there was one detection during the 2 oclock hour -->
        <Time>2000-01-01T02:00:00Z</Time>
        <Count>1</Count>
    </Detection>
</root>

I am just beginning with XSLT. xsl:for-each-group seems to be the function of interest. The difficulty appears to be deriving an hourly DateTime (eg 00:00:00) from a DateTime element's value (eg 00:12:00). The second difficulty is keeping track of how many elements have been binned. So as to not have this simply solved for me, my question is simply:

is this possible(not extremely difficult) to do with XSLT?

any further input or examples are very much appreciated!

Upvotes: 0

Views: 1082

Answers (1)

Tim C
Tim C

Reputation: 70638

If you are only interested in the hourly portion, and are not rounding up/down to the nearest hour, just 'group by' the part of Time that occurs before the first :

<xsl:for-each-group select="Detection" group-by="substring-before(Time, ':')">

Then, it is not a case of 'binning' elements. The current-group() function can be used to get all the Detection that are in your group. For example

<Count><xsl:value-of select="count(current-group())" /></Count>

Try this XSLT

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output indent="yes"/>
    <xsl:template match="root">
        <xsl:copy>
            <xsl:for-each-group select="Detection" group-by="substring-before(Time, ':')">
                <xsl:copy>
                    <Time><xsl:value-of select="substring-before(Time, ':')" />:00:00Z</Time>
                    <Count><xsl:value-of select="count(current-group())" /></Count>
                </xsl:copy>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>
</xsl:transform>

Upvotes: 2

Related Questions