xsl-list
[Top] [All Lists]

Re: Identity Transform Grouping Question

2004-11-09 00:26:05

Hi Ethan,

I added commented patches below..

Cheers,
Geert

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; 
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">

        <!-- xmlns:exsl="http://exslt.org/common"; 
extension-element-prefixes="exsl" -->

        <xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
        <xsl:strip-space elements="*"/>

        <!-- keys for grouping -->
        <xsl:key name="cities" match="cities" use="city[(_at_)lang='en']"/>
        <xsl:key name="offices" match="office" 
use="concat(cities/city[(_at_)lang='en'],'-',names/name[(_at_)lang='en'])"/>

For the index using solution, add:
<xsl:key name="offices-by-country" match="offices" use="../@name"/>


        <xsl:template match="/">
                <xsl:comment> with apply-templates </xsl:comment>
                <xsl:apply-templates/>
        </xsl:template>

        <!-- identity transform: copy all elements -->
        <xsl:template match="*">
                <xsl:copy>
                        <xsl:copy-of select="@*"/>
                        <xsl:apply-templates/>
                </xsl:copy>
        </xsl:template>

        <!-- sort divisions by @id -->
        <xsl:template match="divisions">
                <divisions>
                        <xsl:apply-templates select="division">
                                <xsl:sort select="@id"/>
                        </xsl:apply-templates>
                </divisions>
        </xsl:template>

        <!-- sort regions and countries by @name -->
        <xsl:template match="regions|countries">
                <xsl:copy>
                        <xsl:apply-templates select="region|country">
                                <xsl:sort select="@name"/>
                        </xsl:apply-templates>
                </xsl:copy>
        </xsl:template>

        <!-- reorganize 'country' elements -->
        <xsl:template match="country">
                <country>
                        <xsl:copy-of select="@*"/>
                        <cities>
                                <!-- group 'city' elements within this country 
-->
                                <xsl:variable name="offices-in-this-country">
                                        <xsl:copy-of select="offices"/>
                                        <!-- this is a trick, to restrict all 
subsequent key usage to this country only -->
                                </xsl:variable>
                                <xsl:apply-templates 
select="msxsl:node-set($offices-in-this-country)/offices/office/cities[count(.|key('cities',city[(_at_)lang='en'])[1])=1]">
                                        <xsl:sort 
select="city[(_at_)lang='en']"/>
                                </xsl:apply-templates>

I wonder if something like:

   <xsl:variable name="offices-in-this-country" select="offices" />

wouldn't have worked just as fine.

Note that the select of apply-templates should be changed as well into:

   <xsl:apply-templates
     select="$offices-in-this-country/office
             /cities[count(. | key('cities', city[(_at_)lang='en'])[1]) = 1]">
     ...
   </xsl:apply-templates>

However, this is not the suggestion I tried to make. I said 'why not use an index'. Then, you will no longer need the variable but only the index I added at the top. Change apply-templates into:

   <xsl:apply-templates
     select="key('offices-by-country', @name)/office
             /cities[count(. | key('cities', city[(_at_)lang='en'])[1]) = 1]">
     ...
   </xsl:apply-templates>

                        </cities>
                </country>
        </xsl:template>

        <!-- new 'city' elements -->
        <xsl:template match="cities">
                <city name="{city[(_at_)lang='en']}">
                <!-- (uncomment this if you need multilingual city names in the 
output)
                <city>
                        <xsl:apply-templates select="city" mode="name"/>
                        -->
                        <offices>
                                <!-- group 'office' elements located in this city 
by their english name -->
                                <xsl:variable name="this-city" 
select="city[(_at_)lang='en']"/>
                                <xsl:variable name="offices-in-this-city" 
select="../../../offices/office[cities/city[(_at_)lang='en']=$this-city]"/>
                                <xsl:apply-templates 
select="$offices-in-this-city
                                        [count(.|key('offices', 
concat(cities/city[(_at_)lang='en'],'-',names/name[(_at_)lang='en']))[1])=1]">
                                        <xsl:sort 
select="names/name[(_at_)lang='en']"/>
                                </xsl:apply-templates>
                        </offices>
                </city>
        </xsl:template>

        <!-- new city name elements -->
        <xsl:template match="city" mode="name">
                <name>
                        <xsl:copy-of select="@*"/>
                        <xsl:value-of select="."/>
                </name>
        </xsl:template>

        <!-- new office elements -->
        <xsl:template match="office">
                <office>
                        <xsl:apply-templates select="names/name"/>
                        <!-- add locations for each office with this name -->
                        <xsl:apply-templates select="key('offices',
                                
concat(cities/city[(_at_)lang='en'],'-',names/name[(_at_)lang='en']))" 
mode="location">
                                <xsl:sort select="address"/>
                        </xsl:apply-templates>
                </office>
        </xsl:template>

        <!-- new location elements -->
        <xsl:template match="office" mode="location">
                <location>
                        <xsl:copy-of select="@*"/>
                        <xsl:apply-templates select="address|phone"/>
                </location>
        </xsl:template>

</xsl:stylesheet>


<Prev in Thread] Current Thread [Next in Thread>