xsl-list
[Top] [All Lists]

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

2014-11-18 10:02:27
Given this already normalized input:

<ranges>
  <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"/>
</ranges>

I'm applying a two-pass transformation:

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

  <xsl:template match="/ranges">
    <xsl:variable name="pass1" as="element(pass1)">
      <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"/>
          <range>
            <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()]"/>
          </range>
        </xsl:for-each-group>
      </pass1>
    </xsl:variable>
    <xsl:variable name="pass2" as="element(pass2)">
      <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"/>
      </pass2>
    </xsl:variable>
    <result>
      <xsl:sequence select="$pass1"/>
      <xsl:sequence select="$pass2"/>
    </result>
  </xsl:template>

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

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

</xsl:stylesheet>

to receive this result:

<?xml version="1.0" encoding="UTF-8"?>
<result>
   <pass1>
      <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"/>
   </pass1>
   <pass2>
      <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"/>
   </pass2>
</result>

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.

Gerrit



On 18.11.2014 06:06, Michael Friedman sumarimike(_at_)hotmail(_dot_)com wrote:
Greetings,

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:
<finalrange>
<range>150-200</range>
<range>201-202</range>
etc
</finalrange>
or:
<range start="150" end="200"/>
<range start="201" end="202"/>
etc

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!

Thanks,
Michael Friedman
XSL-List info and archive <http://www.mulberrytech.com/xsl/xsl-list>
EasyUnsubscribe <-list/225679>
(by email <>)

--
Gerrit Imsieke
Geschäftsfü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(_dot_)imsieke(_at_)le-tex(_dot_)de, http://www.le-tex.de

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

Geschäftsführer: Gerrit Imsieke, Svea Jelonek,
Thomas Schmidt, Dr. Reinhard Vöckler
--~----------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
EasyUnsubscribe: http://lists.mulberrytech.com/unsub/xsl-list/1167547
or by email: xsl-list-unsub(_at_)lists(_dot_)mulberrytech(_dot_)com
--~--

<Prev in Thread] Current Thread [Next in Thread>