[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message]

Grouping

Subject: Grouping
From: "Christoph Naber pentium120mhz@xxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Wed, 21 Oct 2020 15:25:17 -0000
 Grouping
Hello xsl-list,

the question of Charlie brought me to thinking about a problem and solution
that I came up with a few years ago.
A few words upfront:
- This is a _very_ stripped down example which doesn't reflect the complete
structure. I hope that I was able to retain all relevant structural
attributes that shape the solution.
- I included <parentA> and <parentB> in the example to account for the fact
that there are at least 20 different parents where the relevant <points>
can occur as childs. It's also possible that a parent holds relevant
<points>, completely different nodes and also another parent.
- I don't want to include <xsl:for-each-group > in every possible parent
for the sake of maintainability. Because of the loads of different child
nodes it seems to me atm that it's not sensible to write specialized
select-statements in apply-templates. Just select="@* | node()" and let the
child-nodes "somehow" take care of themselves.

My solution is somewhat like the following
<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="
http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes" method="xml" encoding="UTF-8"
media-type="text/xml"/>

  <xsl:template match="parentA">
    <xsl:copy>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="parentB">
    <xsl:copy>
      <xsl:apply-templates/>
    </xsl:copy>
    <special/>
  </xsl:template>

  <xsl:template match="@* | node()" priority="-1">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="point[@type='1']">
    <xsl:message>stripped point</xsl:message>
  </xsl:template>

  <!-- in the first position or directly after a node() that is not part of
the group -->
  <xsl:template match="point[@type='1' and ((position() = 1) or
(preceding-sibling::*[1][self::point[not(@type='1')] or
not(self::point)]))]"
    priority="10">
    <xsl:variable name="first-point-not-in-group"
select="following-sibling::*[self::point[not(@type='1')] or
not(self::point)][1]"/>
    <group>
      <!-- apply-templates to self and everything that follows until the
next point that is not part of the group -->
      <xsl:apply-templates select=". |
following-sibling::*[generate-id(self::*) ne
generate-id($first-point-not-in-group) and
      (every $i in preceding-sibling::point satisfies (generate-id($i) ne
generate-id($first-point-not-in-group)))]" mode="grouping"/>
    </group>
  </xsl:template>

  <xsl:template match="node()" mode="grouping">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

I would be interested in ways to tackle this. Are there patterns for this
type of grouping problem? Is it favorable to put "for-each-group" stmts all
over the place? In general: are there better ways to solve this?

The sample input:
<base>
  <parentA>
    <element />
    <point value="A-A" type="1" />
    <point value="A-B" type="1" />
    <point value="A-C" type="2" />
    <point value="A-D" type="1" />
    <point value="A-E" type="2" />
    <point value="A-F" type="1" />
    <point value="A-G" type="1" />
    <point value="A-H" type="1" />
  </parentA>
  <parentB>
    <point value="B-A" type="1" />
    <point value="B-B" type="1" />
    <point value="B-C" type="2" />
    <element />
    <point value="B-D" type="1" />
    <point value="B-E" type="1" />
  </parentB>
</base>

The expected output:

<?xml version="1.0" encoding="UTF-8"?>
<base>
  <parentA>
    <element/>
    <group>
      <point value="A-A" type="1"/>
      <point value="A-B" type="1"/>
    </group>
    <point value="A-C" type="2"/>
    <group>
      <point value="A-D" type="1"/>
    </group>
    <point value="A-E" type="2"/>
    <group>
      <point value="A-F" type="1"/>
      <point value="A-G" type="1"/>
      <point value="A-H" type="1"/>
    </group>
  </parentA>
  <parentB>
    <group>
      <point value="B-A" type="1"/>
      <point value="B-B" type="1"/>
    </group>
    <point value="B-C" type="2"/>
    <element/>
    <group>
      <point value="B-D" type="1"/>
      <point value="B-E" type="1"/>
    </group>
  </parentB>
  <special/>
</base>

Thank you in advance for any insight!

Best regards
Christoph Naber

Current Thread

PURCHASE STYLUS STUDIO ONLINE TODAY!

Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!

Buy Stylus Studio Now

Download The World's Best XML IDE!

Accelerate XML development with our award-winning XML IDE - Download a free trial today!

Don't miss another message! Subscribe to this list today.
Email
First Name
Last Name
Company
Subscribe in XML format
RSS 2.0
Atom 0.3
Site Map | Privacy Policy | Terms of Use | Trademarks
Free Stylus Studio XML Training:
W3C Member
Stylus Studio® and DataDirect XQuery ™are products from DataDirect Technologies, is a registered trademark of Progress Software Corporation, in the U.S. and other countries. © 2004-2013 All Rights Reserved.