Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > XML > How to split a grouping into 3 parts

Reply
Thread Tools

How to split a grouping into 3 parts

 
 
Hvid Hat
Guest
Posts: n/a
 
      03-05-2008
Hi

At first, I thought I could only solve my problem with a C# method inside
my XSLT but I'm beginning to think it might be possible with XSLT only. So
I'm trying, but I need help How can I split a grouping into 3 parts?

I've got the following grouping of countries which is working fine:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlnssl="http://www.w3.org/1999/XSL/Transform">
<xslutput method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="x" match="Country" use="." />
<xsl:template match="Persons">
<xsl:for-each select="Person[generate-id(Country) = generate-id(key('x',
Country)[1])]">
<xsl:value-of select="Country" /><br />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

But how can I split the grouping into 3 parts. Say there's 11 (unique) countries.
How can I split them into 3 parts so I can produce 3 lists of 4, 4 and 3
elements, e.g.

<ul>
<li>Argentina</li>
<li>Belgium</li>
<li>Canada</li>
<li>Denmark</li>
</ul>
<ul>
<li>England</li>
<li>France</li>
<li>Greece</li>
<li>Hungary</li>
</ul>
<ul>
<li>Iceland</li>
<li>Japan</li>
<li>Kenya</li>
</ul


 
Reply With Quote
 
 
 
 
Pavel Lepin
Guest
Posts: n/a
 
      03-06-2008

Hvid Hat <(E-Mail Removed)> wrote in
<(E-Mail Removed)>:
> <?xml version="1.0" encoding="UTF-8"?>
> <xsl:stylesheet version="1.0"
> xmlnssl="http://www.w3.org/1999/XSL/Transform">
> <xslutput method="xml" version="1.0" encoding="UTF-8"
> indent="yes"/> <xsl:key name="x" match="Country" use="."
> /> <xsl:template match="Persons">
> <xsl:for-each select="Person[generate-id(Country) =
> generate-id(key('x',
> Country)[1])]">
> <xsl:value-of select="Country" /><br />
> </xsl:for-each>
> </xsl:template>
> </xsl:stylesheet>
>
> But how can I split the grouping into 3 parts. Say there's
> 11 (unique) countries. How can I split them into 3 parts
> so I can produce 3 lists of 4, 4 and 3 elements, e.g.


Define a grouping key that would allow you to select only
first occurrences of country elements in your document.
Then use count() and position() XPath functions to split it
into n chunks.

--
In Soviet Russia, XML documents transform *you*.
 
Reply With Quote
 
 
 
 
Hvid Hat
Guest
Posts: n/a
 
      03-06-2008
Hello Pavel,

> Define a grouping key that would allow you to select only first
> occurrences of country elements in your document. Then use count() and
> position() XPath functions to split it into n chunks.


I figured, I had to use a combination of position(), count() and the mod
operator to achieve it, but I'm still not comfortable enough with XSLT to
know how to go about it. I've looked at http://www.dpawson.co.uk/xsl/sect2/N4486.html
for grouping by position without luck. I'm just not into the XSLT way of
thinking (yet).

<xsl:stylesheet version="1.0" xmlnssl="http://www.w3.org/1999/XSL/Transform">
<xslutput method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="x" match="Country" use="."/>
<xsl:template match="Persons">
<xsl:for-each select="Person[generate-id(Country) = generate-id(key('x',
Country)[1])]">
<xsl:value-of select="position()"/> of <xsl:value-of select="last()"/> -
<xsl:value-of select="Country"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet


 
Reply With Quote
 
Martin Honnen
Guest
Posts: n/a
 
      03-07-2008
Hvid Hat wrote:

> I figured, I had to use a combination of position(), count() and the mod
> operator to achieve it, but I'm still not comfortable enough with XSLT
> to know how to go about it. I've looked at
> http://www.dpawson.co.uk/xsl/sect2/N4486.html for grouping by position
> without luck. I'm just not into the XSLT way of thinking (yet).


Here is an example XML document:

<root>
<country>USA</country>
<country>Germany</country>
<country>France</country>
<country>Russia</country>
<country>Spain</country>
<country>Russia</country>
<country>Italy</country>
<country>Canada</country>
<country>USA</country>
<country>Portugal</country>
<country>France</country>
<country>Denmark</country>
<country>Sweden</country>
</root>

Here is an XSLT 1.0 stylesheet that uses Muenchian grouping to save the
unique countries in a variable, then the exslt:node-set function
(supported by XslCompiledTransform) to get a node-set to be split into
three components:

<xsl:stylesheet
xmlnssl="http://www.w3.org/1999/XSL/Transform"
xmlns:exslt="http://exslt.org/common"
exclude-result-prefixes="exslt"
version="1.0">

<xslutput method="html" indent="yes"/>

<xslaram name="number-of-parts" select="3"/>

<xsl:key name="by-country" match="country" use="."/>

<xsl:variable
name="unique-countries-rtf">
<xsl:for-each
select="/root/country[generate-id() =
generate-id(key('by-country', .)[1])]">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>

<xsl:variable
name="unique-countries"
select="exslt:node-set($unique-countries-rtf)/country"/>

<xsl:variable name="items-per-list"
select="ceiling(count($unique-countries) div $number-of-parts)"/>

<xsl:template match="root">
<div>
<xsl:apply-templates
select="$unique-countries[position() mod $items-per-list = 1]"
mode="group"/>
</div>
</xsl:template>

<xsl:template match="country" mode="group">
<ul>
<xsl:apply-templates
select=". | following-sibling::country[position() &lt;
$items-per-list]"
mode="item"/>
</ul>
</xsl:template>

<xsl:template match="country" mode="item">
<li>
<xsl:value-of select="."/>
</li>
</xsl:template>

</xsl:stylesheet>

Result looks like this:

<div>
<ul>
<li>USA</li>
<li>Germany</li>
<li>France</li>
<li>Russia</li>
</ul>
<ul>
<li>Spain</li>
<li>Italy</li>
<li>Canada</li>
<li>Portugal</li>
</ul>
<ul>
<li>Denmark</li>
<li>Sweden</li>
</ul>
</div>


If you don't want to use exslt:node-set then I think you need to chain
two stylesheets where the first creates a list with the unique countries
and the second splits them into three groups.

--

Martin Honnen
http://JavaScript.FAQTs.com/
 
Reply With Quote
 
Pavel Lepin
Guest
Posts: n/a
 
      03-07-2008

Martin Honnen <(E-Mail Removed)> wrote in
<47d1372f$0$10999$(E-Mail Removed)-online.net>:
> Here is an example XML document:
>
> <root>
> <country>USA</country>
> <country>Germany</country>
> <country>France</country>
> <country>Russia</country>
> <country>Spain</country>
> <country>Russia</country>
> <country>Italy</country>
> <country>Canada</country>
> <country>USA</country>
> <country>Portugal</country>
> <country>France</country>
> <country>Denmark</country>
> <country>Sweden</country>
> </root>


[transformation snipped]

> If you don't want to use exslt:node-set then I think you
> need to chain two stylesheets where the first creates a
> list with the unique countries and the second splits them
> into three groups.


Not at all.

<xsl:stylesheet version="1.0"
xmlnssl="http://www.w3.org/1999/XSL/Transform">
<xslutput method="xml" indent="yes"/>
<xsl:key name="countries" match="/root/country"
use="string(.)"/>
<xsl:key name="unique-countries" match="/root/country"
use="count(.|key('countries',.)[1])"/>
<xsl:variable name="unique-countries"
select="key('unique-countries',1)"/>
<xsl:variable name="parts" select="3"/>
<xsl:template match="root">
<result>
<xsl:apply-templates select="$unique-countries"
mode="group">
<xsl:with-param name="parts" select="$parts"/>
<xsl:with-param name="elts"
select="$unique-countries"/>
</xsl:apply-templates>
</result>
</xsl:template>
<xsl:template match="/root/country" mode="group">
<xslaram name="parts"/>
<xslaram name="elts"/>
<xsl:variable name="total" select="count($elts)"/>
<xsl:variable name="per-group"
select="ceiling($total div $parts)"/>
<xsl:variable name="pos" select="position()-1"/>
<xsl:variable name="group"
select="floor($pos div $per-group)"/>
<xsl:if test="$pos mod $per-group=0">
<group>
<xsl:apply-templates
select=
"
$elts
[floor((position()-1) div $per-group)=$group]
"/>
</group>
</xsl:if>
</xsl:template>
<xsl:template match="/root/country">
<list-elt>
<xsl:apply-templates/>
</list-elt>
</xsl:template>
</xsl:stylesheet>

Xalan will choke on this due to key() invocation in a use
attribute of an xsl:key element, of course. libxslt and
Saxon won't.

--
In Soviet Russia, XML documents transform *you*.
 
Reply With Quote
 
Martin Honnen
Guest
Posts: n/a
 
      03-07-2008
Pavel Lepin wrote:

> Xalan will choke on this due to key() invocation in a use
> attribute of an xsl:key element, of course. libxslt and
> Saxon won't.


Saxon 6 complains too: "key() function cannot be used here". .NET's
XslCompiledTransform also complains: "The 'key()' function cannot be
used in 'use' and 'match' attributes of 'xsl:key' element.".
And with Saxon 9 and XSLT 2 I don't think you need keys at all, you can
use xsl:for-each-group and temporary trees if needed.


--

Martin Honnen
http://JavaScript.FAQTs.com/
 
Reply With Quote
 
Pavel Lepin
Guest
Posts: n/a
 
      03-07-2008

Martin Honnen <(E-Mail Removed)> wrote in
<47d14595$0$11004$(E-Mail Removed)-online.net>:
> Pavel Lepin wrote:
>> Xalan will choke on this due to key() invocation in a use
>> attribute of an xsl:key element, of course. libxslt and
>> Saxon won't.

>
> Saxon 6 complains too: "key() function cannot be used
> here". .NET's XslCompiledTransform also complains: "The
> 'key()' function cannot be used in 'use' and 'match'
> attributes of 'xsl:key' element.". And with Saxon 9 and
> XSLT 2 I don't think you need keys at all, you can use
> xsl:for-each-group and temporary trees if needed.


The differences are due to unclear status of E13 in XSLT 1.0
Errata. Frankly, I think the idea is ridiculous. The WG
tried to remove an incredibly useful feature, using 'cycle
prevention' as a justification. Hell, why not disallow
recursion? it might be infinite, after all.

--
In Soviet Russia, XML documents transform *you*.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: How include a large array? Edward A. Falk C Programming 1 04-04-2013 08:07 PM
most elegant way to split text file randomly into n parts? Markus Dehmann Perl Misc 6 12-14-2007 09:07 PM
Can I use Photoshop "Slice" to simply split an image into many parts? terry@terryking.us Digital Photography 20 10-25-2005 01:18 PM
split a string with quoted parts into list oliver Python 5 03-11-2005 05:31 AM
Parts parts....PARTS!!! ARGHHH dstvns A+ Certification 8 01-07-2004 07:57 PM



Advertisments