Reputation: 335
This is my first question here.
I want to transform this xml using XSL 1.0 :
<RESULTS>
<RES>
<GROUP>1</GROUP>
<SUBGROUP>A</SUBGROUP>
<NAME>Alice</NAME>
</RES>
<RES>
<GROUP>1</GROUP>
<SUBGROUP>A</SUBGROUP>
<NAME>Bart</NAME>
</RES>
<RES>
<GROUP>1</GROUP>
<SUBGROUP>B</SUBGROUP>
<NAME>Keira</NAME>
</RES>
<RES>
<GROUP>2</GROUP>
<SUBGROUP>A</SUBGROUP>
<NAME>Mike</NAME>
</RES>
<RES>
<GROUP>2</GROUP>
<SUBGROUP>B</SUBGROUP>
<NAME>Peter</NAME>
</RES>
<RES>
<GROUP>2</GROUP>
<SUBGROUP>B</SUBGROUP>
<NAME>Olaf</NAME>
</RES>
</RESULTS>
Into this:
<h1> 1 </h1>
<h2>A</h2>
<p>Alice</p>
<p>Bart</p>
<h2>B</h2>
<p>Keira</p>
<h1> 2 </h1>
<h2>A</h2>
<p>Mike</p>
<h2>B</h2>
<p>Peter</p>
<p>Olaf</p>
I already tried using Muenchian Method, however this only allowed me to sort by GROUP, and I could not sort the sorted results by SUBGROUP. Note that I have to view the header only once per group/subgroup.
@ C. M. Sperberg-McQueen
I did not want to post a wall of text, but if it might help I do it:
This is one of the solutions I have tried:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="byGROUP" match="RESULTS/RES" use="GROUP" />
<xsl:template match="RESULTS">
<xsl:for-each select="RES[count(. | key('byGROUP', GROUP)[1]) = 1]">
<xsl:sort select="GROUP" order="descending" />
<h1>
<xsl:value-of select="GROUP" />
</h1>
<xsl:for-each select="key('byGROUP', GROUP)">
<xsl:sort select="SUBGROUP" order="descending" />
<h2>
<xsl:value-of select="SUBGROUP" />
</h2>
<p>
<xsl:value-of select="NAME" />
</p>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I tried using preceiding-sibling to test whether to view the SUBGROUP but I found it imposible to iterate through the nodes, so perhaps it is not a good approach.
Upvotes: 2
Views: 762
Reputation: 101738
The typical way to do multiple groupings is to use the concatenation of the current level's value with all of the parent values as the key value:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="byGROUP" match="RESULTS/RES" use="GROUP" />
<xsl:key name="bySUBGROUP" match="RESULTS/RES"
use="concat(GROUP, '+', SUBGROUP)" />
<xsl:template match="RESULTS">
<xsl:apply-templates
select="RES[count(. | key('byGROUP', GROUP)[1]) = 1]
/GROUP">
<xsl:sort select="." order="ascending" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="GROUP">
<h1>
<xsl:value-of select="." />
</h1>
<xsl:variable name="thisGroup" select="key('byGROUP', .)" />
<xsl:apply-templates
select="$thisGroup[count(. |
key('bySUBGROUP', concat(GROUP, '+', SUBGROUP))[1])
= 1]
/SUBGROUP">
<xsl:sort select="." order="ascending" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="SUBGROUP">
<h2>
<xsl:value-of select="." />
</h2>
<xsl:apply-templates select="key('bySUBGROUP', concat(../GROUP, '+', .))"/>
</xsl:template>
<xsl:template match="RES">
<p>
<xsl:value-of select="NAME" />
</p>
</xsl:template>
</xsl:stylesheet>
When run on your sample input, this produces:
<h1>1</h1>
<h2>A</h2>
<p>Alice</p>
<p>Bart</p>
<h2>B</h2>
<p>Keira</p>
<h1>2</h1>
<h2>A</h2>
<p>Mike</p>
<h2>B</h2>
<p>Peter</p>
<p>Olaf</p>
Upvotes: 2