Listing distinct nodes and sibling elements

C

Chris

I have a problem with grouping elements in my XSL. What I want to do
is select all of the distinct SCE_CGPC records, then by the FSG_SNAM
records for each SCE_CGPC and then again by MOA_NAME for each SCE_CGPC
and FSG_SNAM.

I have included a achunk of my XML to help expalin this:

This is a chunk of the XML file

<z:row SCE_CGPC='PGRE' FSG_SNAM='HOME' MOA_NAME='Full time'

<z:row SCE_CGPC='PGRE' FSG_SNAM='OVERSEAS' MOA_NAME='Full time'

<z:row SCE_CGPC='PGRE' FSG_SNAM='HOME MOA_NAME='Full time'

<z:row SCE_CGPC='PGRE' FSG_SNAM='HOME' MOA_NAME='Part-Time'

<z:row SCE_CGPC='PGRE' FSG_SNAM='OVERSEAS' MOA_NAME='Full time'

So for this chunk of data I would like to see the following:

PGRE - HOME - FULL TIME PART TIME
- OVERSEAS - FULL TIME

So effectively what I am trying to do is group by distinct (SCE_CGPC,
FSG_SNAM, MOA_NAME)

I have managed to get the distinct list of SCE_CGPC using the
following:








<xsl:key name="group-by-cgpc" match="/xml/rs:data/z:row"
use="@SCE_CGPC"/>

<xsl:template match="/">
<xsl:for-each select="/xml/rs:data/z:row[generate-id(.)=
generate-id(key('group-by-cgpc',@SCE_CGPC))]/@SCE_CGPC">
<xsl:value-of select="."/>


<xsl:call-template name="firstlevel">
<xsl:with-param name="coursegroupcode" select="." />
</xsl:call-template>
</xsl:for-each>
</xsl:template>


<xsl:template name="firstlevel">
<xsl:param name="coursegroupcode" />
<xsl:value-of select="concat('', @SCE_CGPC,' - There are
',count(key('group-by-cgpc', $coursegroupcode)),' records:')"/><br/>


</xsl:template>
</xsl:stylesheet>



But what this does not to do and where I am struggling is getting the
vales of FSG_SNAM for each of the SCE_CGPC's and the values of MOA_NAME
for each FSG_SNAM.

Any help would be greatly appreciated
Chris
 
J

Joris Gillis

I have a problem with grouping elements in my XSL. What I want to do
is select all of the distinct SCE_CGPC records, then by the FSG_SNAM
records for each SCE_CGPC and then again by MOA_NAME for each SCE_CGPC
and FSG_SNAM.

I have included a achunk of my XML to help expalin this:

This problem is easily solved with concat-augmented muenchian grouping
(http://www.dpawson.co.uk/xsl/sect2/N4486.html#d5171e685).

However, a generic solution to group nodes to an arbitrary level in XSLT
1.0 is quite tempting.
Here's my attempt:



==input==
<xml xmlns:z="z" xmlns:rs="rs">
<rs:data>
<z:row SCE_CGPC='PGRE' FSG_SNAM='HOME' MOA_NAME='Full time'/>
<z:row SCE_CGPC='PGRE' FSG_SNAM='OVERSEAS' MOA_NAME='Full time'/>
<z:row SCE_CGPC='PGRE' FSG_SNAM='HOME MOA_NAME' MOA_NAME='Full time'/>
<z:row SCE_CGPC='PGRE' FSG_SNAM='HOME' MOA_NAME='Part-Time'/>
<z:row SCE_CGPC='PGRE' FSG_SNAM='OVERSEAS' MOA_NAME='Full time'/>
</rs:data>
</xml>

==XSLT==
<?xml version='1.0' encoding='utf-8' ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0" xmlns:z="z" xmlns:rs="rs">
<xsl:eek:utput method="xml" indent="yes"/>

<xsl:key name="row" match="z:row" use="@SCE_CGPC"/>
<xsl:key name="row" match="z:row" use="@FSG_SNAM"/>
<xsl:key name="row" match="z:row" use="@MOA_NAME"/>

<xsl:variable name="keys"
select="document('')/xsl:stylesheet/xsl:key[@name='row']/@use"/>

<xsl:template match="rs:data">
<grouped>
<xsl:call-template name="group"/>
</grouped>
</xsl:template>

<xsl:template match="z:row">
<xsl:copy-of select="."/>
</xsl:template>

<xsl:template name="group">
<xsl:param name="level" select="1"/>
<xsl:param name="nodes" select="/xml/rs:data/z:row"/>
<xsl:param name="groupkey" select="$keys[$level]"/>
<xsl:param name="groupkeys" select="$keys[position() &lt;= $level ]"/>
<xsl:for-each
select="/xml/rs:data/z:row[generate-id(.)=generate-id(key('row',@*[concat('@',name())=$groupkey]))]">
<group foo="{count($nodes)}" level="{$level}"
groupcode="{concat($groupkey,'=',@*[concat('@',name())=$groupkey])}">
<xsl:for-each
select="$nodes[@*[concat('@',name())=$groupkey]=current()/@*[concat('@',name())=$groupkey]][1]">
<xsl:variable name="all"
select="key('row',@*[concat('@',name())=$groupkeys])"/>
<xsl:variable name="set"
select="key('row',@*[concat('@',name())=$groupkeys])|dummy"/>
<xsl:variable name="filter">
<xsl:for-each select="$set">
<xsl:variable name="numoccur"
select="count($all[generate-id(.)=generate-id(current())])"/>
<xsl:if test="$numoccur=$level">+</xsl:if>
<xsl:if test="not($numoccur=$level)">-</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="pre_filtered"
select="$set[substring($filter,position(),1)='+']"/>
<xsl:variable name="filtered"
select="$nodes[count(.|$pre_filtered)=count($pre_filtered)]"/>
<xsl:if test="$level = 3">
<xsl:apply-templates select="$filtered"/>
</xsl:if>
<xsl:if test="$level != 3">
<xsl:call-template name="group">
<xsl:with-param name="level" select="$level + 1" />
<xsl:with-param name="nodes" select="$filtered" />
</xsl:call-template>
</xsl:if>
</xsl:for-each>
</group>
</xsl:for-each>
</xsl:template>

</xsl:stylesheet>

==output==

<?xml version="1.0" encoding="UTF-8"?>
<grouped xmlns:rs="rs" xmlns:z="z">
<group foo="5" level="1" groupcode="@SCE_CGPC=PGRE">
<group foo="5" level="2" groupcode="@FSG_SNAM=HOME">
<group foo="2" level="3" groupcode="@MOA_NAME=Full time">
<z:row SCE_CGPC="PGRE" FSG_SNAM="HOME" MOA_NAME="Full time"/>
</group>
<group foo="2" level="3" groupcode="@MOA_NAME=Part-Time">
<z:row SCE_CGPC="PGRE" FSG_SNAM="HOME" MOA_NAME="Part-Time"/>
</group>
</group>
<group foo="5" level="2" groupcode="@FSG_SNAM=OVERSEAS">
<group foo="2" level="3" groupcode="@MOA_NAME=Full time">
<z:row SCE_CGPC="PGRE" FSG_SNAM="OVERSEAS" MOA_NAME="Full time"/>
<z:row SCE_CGPC="PGRE" FSG_SNAM="OVERSEAS" MOA_NAME="Full time"/>
</group>
<group foo="2" level="3" groupcode="@MOA_NAME=Part-Time"/>
</group>
<group foo="5" level="2" groupcode="@FSG_SNAM=HOME MOA_NAME">
<group foo="1" level="3" groupcode="@MOA_NAME=Full time">
<z:row SCE_CGPC="PGRE" FSG_SNAM="HOME MOA_NAME" MOA_NAME="Full time"/>
</group>
<group foo="1" level="3" groupcode="@MOA_NAME=Part-Time"/>
</group>
</group>
</grouped>

This code relies on the observation that the xslt 'key' function does not
remove duplicates in the node-set it returns.
It is possible that this observation is a result of an erroneous
implementation of my xslt-processor. This would of course render the
solution useless...

regards,
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top