[xsl] Grouping problem

Subject: [xsl] Grouping problem
From: "Ruggier, Mario" <Mario.Ruggier@xxxxxxxxxxxxxxxx>
Date: Mon, 22 Jul 2002 18:55:56 +0200
Hello, 

I had mailed some time ago about this grouping problem,
but my original description of it was too simplistic.

Here's the more complicated version: I would like to 
convert the list of <obj> elements (see INPUT below)
to the hierarchical XML (see OUTPUT below).

The XSL that provides a partial solution to this 
(from Joerg Heinicke) is also below. I would like to either 
generalize this to support sub-branching (as shown by the 
output for "fr") or to find another algorithm.

Many thanks!

Mario Ruggier 


===================================
INPUT XML (<obj> elements may occur in any order): 

<r>
 <obj>
   <p.array size="1">
     <v>en</v>     
   </p.array>
 </obj>
  <obj>
   <p.array size="1">
     <v>it</v>     
   </p.array>
 </obj>
 <obj>
   <p.array size="1">
     <v>fr</v>     
   </p.array>
 </obj>
 <obj>
   <p.array size="2">
     <v>one</v>
     <v>en</v>     
   </p.array>
 </obj>
 <obj>
   <p.array size="2">
     <v>uno</v>
     <v>it</v>     
   </p.array>
 </obj>
  <obj>
   <p.array size="2">
     <v>un</v>
     <v>fr</v>     
   </p.array>
 </obj>
 <obj>
   <p.array size="3">
     <v>two</v>
     <v>one</v>
     <v>en</v>     
   </p.array>
 </obj>
 <obj>
   <p.array size="3">
     <v>due</v>
     <v>uno</v>
     <v>it</v>     
   </p.array>
 </obj>
 <obj>
   <p.array size="3">
     <v>deux</v>
     <v>un</v>     
     <v>fr</v> 
   </p.array>
 </obj>  
 <obj>
   <p.array size="3">
     <v>doo</v>
     <v>un</v>
     <v>fr</v>     
   </p.array>
 </obj> 
 <obj>
   <p.array size="4">
     <v>trois</v>
     <v>doo</v>
     <v>un</v>     
     <v>fr</v> 
   </p.array>
 </obj>
 <obj>
   <p.array size="4">
     <v>3</v>
     <v>doo</v>
     <v>un</v>     
     <v>fr</v> 
   </p.array>
 </obj> 

</r>

===================================
OUTPUT (order of sibling <obj> elements is not important):

<r>
   <obj name="en">
      <obj name="one">
         <obj name="two"/>
      </obj>
   </obj>
   <obj name="it">
      <obj name="uno">
         <obj name="due"/>
      </obj>
   </obj>
   <obj name="fr">
      <obj name="un">
         <obj name="deux" />
         <obj name="doo">
            <obj name="trois"/>
            <obj name="3"/>
         </obj>
      </obj>
   </obj>
</r>

===================================
Partial soloution XSL:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>

<xsl:output indent="yes"/>
<!-- grouping key -->
<xsl:key name="arrays" match="p.array" use="v[last()]"/>

<xsl:template match="r">
  <xsl:copy>
  <!-- select unique <p.array>s, grouped by it's language (last <v>) -->
    <xsl:apply-templates 
          select="obj/p.array[count( . |key('arrays',v[last()])[1] ) = 1]" 
          mode="unique"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="p.array" mode="unique">
  <!-- sort all <p.array>s with this language -->
  <xsl:apply-templates select="key('arrays', v[last()])">
    <!-- alternatively select="count(v)" if no @size-attribute -->
    <xsl:sort select="@size" order="descending"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="p.array">
  <!-- select only the <p.array> of this language with the most entries -->
  <xsl:if test="position() = 1">
    <!-- begin with the last <v>, the language -->
    <xsl:apply-templates select="v[last()]"/>
  </xsl:if>
</xsl:template>

<xsl:template match="v">
  <obj name="{text()}">
    <!-- select the <v> backwards for order 1, 2, 3 and so on -->
    <xsl:apply-templates select="preceding-sibling::v[1]"/>
  </obj>
</xsl:template>
</xsl:stylesheet>

===================================

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread