Listing distinct nodes and sibling elements

Discussion in 'XML' started by Chris, Jun 19, 2006.

  1. Chris

    Chris Guest

    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
     
    Chris, Jun 19, 2006
    #1
    1. Advertising

  2. Chris

    Joris Gillis Guest

    On Mon, 19 Jun 2006 12:56:42 +0200, Chris <> wrote:

    > 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,
    --
    Joris Gillis (http://users.telenet.be/root-jg/me.html)
    «Εν οίδα ότι ουδέν οίδα» - ΣωκÏατης
     
    Joris Gillis, Jun 19, 2006
    #2
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    1
    Views:
    2,184
    =?Utf-8?B?YnJpYW5zW01DU0Rd?=
    Feb 16, 2006
  2. Gerald Aichholzer
    Replies:
    2
    Views:
    2,550
    Gerald Aichholzer
    Jun 27, 2006
  3. Replies:
    11
    Views:
    659
    Fredrik Lundh
    Jan 23, 2006
  4. Replies:
    3
    Views:
    490
  5. Hicham Mouline
    Replies:
    1
    Views:
    407
    Kai-Uwe Bux
    Apr 11, 2010
Loading...

Share This Page