Re: [xsl] Creating new, distinct groups of ranges from an aggregation of individual ranges

Subject: Re: [xsl] Creating new, distinct groups of ranges from an aggregation of individual ranges
From: "Imsieke, Gerrit, le-tex gerrit.imsieke@xxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Tue, 18 Nov 2014 16:02:17 -0000
Given this already normalized input:

  <range start="150" end="202" n="1"/>
  <range start="201" end="225" n="2"/>
  <range start="201" end="204" n="3"/>
  <range start="205" end="234" n="4"/>
  <range start="226" end="234" n="5"/>
  <range start="250" end="260" n="6"/>

I'm applying a two-pass transformation:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="";
  <xsl:output indent="yes"/>

<xsl:template match="/ranges">
<xsl:variable name="pass1" as="element(pass1)">
<!-- eliminates duplicate start/end attributes
and creates a new range for each distinct start
and end value: -->
<xsl:for-each-group select="range/(@start|@end)"
group-by="string-join((name(), .), '=')">
<xsl:sort select="." data-type="number"/>
<xsl:sequence select="."/>
<!-- adds a start attribute to the end attribute
iff the start attribute's value is smaller than
the end attribute's: -->
<xsl:sequence select="../@start[. &lt; current()]"/>
<xsl:variable name="pass2" as="element(pass2)">
<!-- Replaces the existing start attribute with the nearest
value available, looking behind (template with xml:id="B" below).
Ranges without an end attribute will
be eliminated by virtue of the built-in template.
Except for the first range (template with xml:id="A" below):
If it doesn't have an end attribute, and if its start attribute
is not equal to the next range's start attribute, the next
suitable end attribute will be added. -->
<xsl:apply-templates select="$pass1/range" mode="pass2"/>
<xsl:sequence select="$pass1"/>
<xsl:sequence select="$pass2"/>

  <xsl:template mode="pass2" xml:id="A"
    match="range[1][not(@start = following-sibling::*[1]/@start)]">
      <xsl:copy-of select="@start"/>
      <xsl:attribute name="end"
        select="following-sibling::range[@start][1]/@start - 1"/>

  <xsl:template match="range[@end]" mode="pass2" xml:id="B">
      <xsl:attribute name="start"
                     preceding-sibling::*[@end][1]/@end + 1))"/>
      <xsl:copy-of select="@end"/>


to receive this result:

<?xml version="1.0" encoding="UTF-8"?>
      <range start="150"/>
      <range start="201"/>
      <range end="202" start="150"/>
      <range end="204" start="201"/>
      <range start="205"/>
      <range end="225" start="201"/>
      <range start="226"/>
      <range end="234" start="205"/>
      <range start="250"/>
      <range end="260" start="250"/>
      <range start="150" end="200"/>
      <range start="201" end="202"/>
      <range start="203" end="204"/>
      <range start="205" end="225"/>
      <range start="226" end="234"/>
      <range start="250" end="260"/>

It doesn't feel particularly elegant. It almost has
a slightly imperative feel about it, and it might
appear inefficient at first due to the selections along
the sibling axes. But the processor will probably see
that these lookups will stop after the first sibling
and therefore I think it should be sufficient.

You might want to test whether all fringe cases have
been taken into account. For example, change the first
range's end attribute to something less than 201.


On 18.11.2014 06:06, Michael Friedman sumarimike@xxxxxxxxxxx wrote:

I'm trying to use XSLT 2.0 to create a new set of grouped ranges based
on the overlap of an aggregation of a set of non-contiguous individual
ranges. Example:

Given a range of numbers as an individual set:
1. <range>150-202</range>
2. <range>201-225</range>
3. <range>201-204</range>
4. <range>205-234</range>
5. <range>226-234, 250-260</range>

I'm trying to produce a new grouping based on the way the groups overlap:
150-200 (this is where <range> 1 starts and overlaps to 2 & 3)
201-202 (this is where 1 & 2 overlap, and group 1 ends)
203-204 (this is where 2 & 3 overlap and 3 ends)
205-225 (this is where 4 starts and begins to overlap with 5)
226-234 (this is where 4 & 5 overlap and end for the first part of 5)
250-260 (this is where the second range in 5 exists)

The start and end point of the individual source ranges form the boundaries.

I expect to end up with a string or variable structure like:
<range start="150" end="200"/>
<range start="201" end="202"/>

Ultimately I have to format some content in XSLFO based on the XML's
participation in the "new" given range grouping. If you know aircraft
effectivity, this is what I am trying to group.

I've been using <xsl:sequence> to find all the numbers of a single
range, so I can do compares against individual numbers in the entire
range, if necessary. But, it seems like it may be easier to just work
with the boundaries: the start and end points and see if a value falls
within it, somehow, rather than iterating repetitively through
enumerations of sequences.

I've been searching the archives for a while and have found some
evocative possibilities from Dimitre Novatchev and Michael Kay, but I
can't quite find a way to work with the overlapping. I'm continuing to
study their ranging/grouping examples, but help would be appreciated!

Michael Friedman
XSL-List info and archive <>
EasyUnsubscribe <-list/225679>
(by email <>)

-- Gerrit Imsieke GeschC$ftsfC<hrer / Managing Director le-tex publishing services GmbH Weissenfelser Str. 84, 04229 Leipzig, Germany Phone +49 341 355356 110, Fax +49 341 355356 510 gerrit.imsieke@xxxxxxxxx,

Registergericht / Commercial Register: Amtsgericht Leipzig
Registernummer / Registration Number: HRB 24930

GeschC$ftsfC<hrer: Gerrit Imsieke, Svea Jelonek,
Thomas Schmidt, Dr. Reinhard VC6ckler

Current Thread