xsl-list
[Top] [All Lists]

RE: Complicated grouping and column question

2005-06-29 14:08:59
I just did this exact same thing. My solution was to find middle child of the 
parent elements, and identify it with a key, then put all the 
preceding-siblings of that child's parent, plus the parent itself in the left 
hand column, and all the following-siblings of that child's parent in the right 
hand column. It works pretty well, but since the number of parents is variable 
and in the output the spacing between them is greater than the spacing between 
children, the lengths of the columns can be pretty uneven depending on the 
particular file. I've thought a little bit about trying to account for the 
number of parents to correct for this, but it's not a major priority in my 
application. Might be in yours, however.

Sample of my XML:

        <rec_gear_sec>
                <rec_gear_title>Official Papers</rec_gear_title>
                <rec_gear>Valid passport</rec_gear>
                <rec_gear>Tourist visa (given upon arrival in New 
Zealand)</rec_gear>
                <rec_gear>Airline tickets</rec_gear>
        </rec_gear_sec>
        <rec_gear_sec>
                <rec_gear_title>Luggage</rec_gear_title>
                <rec_gear>Duffel bag (wheels and retractable handle are fine), 
sturdy and large enough to hold clothing and gear</rec_gear>
                <rec_gear>Daypack, sized to carry your camera, water bottles 
and extra clothing</rec_gear>
                <rec_gear>Passport security pouch or belt</rec_gear>
                <rec_gear>Luggage tags and luggage locks</rec_gear>
        </rec_gear_sec>
        <rec_gear_sec>
                <rec_gear_title>Camping Gear</rec_gear_title>
                <rec_gear>Sleeping bag; rated to 30-40°F, compressible w/stuff 
sack (if you prefer to bring your own)</rec_gear>
        </rec_gear_sec>
        <rec_gear_sec>
                <rec_gear_title>City Clothing</rec_gear_title>
                <rec_gear>Lightweight, easily washable items for city wear or 
when traveling</rec_gear>
        </rec_gear_sec>
        <rec_gear_sec>
                <rec_gear_title>Clothing</rec_gear_title>
                <rec_gear>T-shirts</rec_gear>
                <rec_gear>Long sleeve shirts</rec_gear>
                <rec_gear>Hiking pants</rec_gear>
                <rec_gear>Hiking shorts</rec_gear>
                <rec_gear>Lightweight fleece or synthetic top</rec_gear>
                <rec_gear>Underwear</rec_gear>
                <rec_gear>Lightweight thermal underwear top and bottom, 
synthetic or wool</rec_gear>
                <rec_gear>Hiking and liner socks</rec_gear>
                <rec_gear>Swimsuit</rec_gear>
        </rec_gear_sec>

My XSL (relevant parts):

<xsl:variable name="gearMid" 
select="generate-id((//rec_gear_sec/*)[round(count(//rec_gear_sec/*) div 2)])"/>

        <xsl:template name="rec_gear_list">
                <xsl:for-each select="//rec_gear_sec/*[generate-id(.) = 
$gearMid]">
                        <div style="width:275; float:left;padding-right:10px;">
                                <xsl:apply-templates 
select="(../preceding-sibling::*)[name()='rec_gear_sec']"/>
                                <xsl:apply-templates select=".."/>
                        </div>
                        <div style="width:285; float:right;">
                                <xsl:apply-templates 
select="(../following-sibling::*)[name()='rec_gear_sec']"/>
                        </div>
                </xsl:for-each>
        </xsl:template>

The parent 'rec_gear_sec' elements also have unrelated siblings with different 
names, so I had to filter in the predicate by name() value.

Hope that gets you pointed in a worthwhile direction.

-- Brook


-----Original Message-----
From: Will McCutchen [mailto:mccutchen(_at_)gmail(_dot_)com] 
Sent: Wednesday, June 29, 2005 1:56 PM
To: xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com
Subject: [xsl] Complicated grouping and column question


Hi everyone,

I've got a problem trying to create two columns for an index of data. 
I can't for the life of me figure out how to break the data over two columns.

This might be hard for me to explain, so please bear with me.

I've got a large XML file representing a course schedule for the community 
college where I work.  Here's a small sample of the format:

<schedule>
    <division name="Business">
        <cluster name="Ethics">
            <course title="..." />
            <course title="..." />
            <course title="..." />
        </cluster>
        <cluster name="Accounting">
            <course title="..." />
        </cluster>
    </division>
    
    <division name="Arts">
        <cluster name="Painting">
            <course title="..." />
        </cluster>
        <cluster name="Drawing">
            <course title="..." />
        </cluster>
    </division>
</schedule>

(The actual format is a little bit more complicated than that, but this is all 
of the structure that's involved in creating the index.)

I've got a stylesheet that creates a nice alphabetical index based on the 
schedule XML file.  For the sample above, the index would look like this:

A
        Arts
                Painting
                Drawing
B
        Business
                Accounting
                Ethics

So, in the index, the top level is a letter from A-Z, the second level is a 
<division> name and the third level is a <cluster> name.

I'm using XSLT 2.0 and Saxon 8.4.  Here's the important part of the current 
stylesheet (which is just outputting a simple HTML unordered list, at the 
moment):

<xsl:template name="make-index">
        <ul>
        <xsl:for-each-group select="/schedule/division" 
group-by="substring(@name,1,1)">
            <xsl:sort select="current-grouping-key()" />
            <xsl:variable name="current-letter" select="current-grouping-key()" 
/>
            
            <li>
                <h2><xsl:value-of select="$current-letter" /></h2>
                <ul>
                    <xsl:apply-templates
select="/schedule/division[substring(@name,1,1) = $current-letter]">
                        <xsl:sort select="@name" />
                    </xsl:apply-templates>
                </ul>
            </li>
        </xsl:for-each-group>
    </ul>
</xsl:template>

<xsl:template match="division">
    <li>
        <xsl:value-of select="@name" />
        <xsl:if test="cluster">
            <ul>
                <xsl:apply-templates select="cluster">
                    <xsl:sort select="@name" />
                </xsl:apply-templates>
            </ul>
        </xsl:if>
    </li>
</xsl:template>

<xsl:template match="cluster">
        <li><xsl:value-of select="@name" /></li>
</xsl:template>


Because I'm using for-each-group to break the divisions up into sections by 
their first letters, I just can't figure out how to calculate a good place to 
break the output into two columns.  The columns need to break on a letter, so 
the "C" section couldn't break at the bottom of one column and continue at the 
top of the other.  One other problem is that the index is weighted heavily 
towards the front of the alphabet, so the break will probably need to come 
around D or E.

If this post made any sense and anyone has any suggestions, I'll be grateful.  
If I need to explain myself better, or provide a complete input document, I'd 
be glad to do so.

Thanks in advance for your help,


Will.

--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--


--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--