Re: Grouping, Counting, and Sorting

Subject: Re: Grouping, Counting, and Sorting
From: "Thomas F. O'Connell" <tfo@xxxxxxxxxxxxxx>
Date: Fri, 04 Jun 2004 16:45:41 -0500
Okay. I've reworked my original approach somewhat and am closer to a
satisfactory solution, but I'm still looking for a little deeper
understanding of a few concepts and, even better, a more complete
solution.

So, now, based on the same example XML provided below, I'm doing this:

<table>
	<th>Header</th>
	<xsl:apply-templates select="node[valid = 1 and position() &lt;= 100]" />
</table>

<xsl:for-each select="node[valid = 1 and position() mod 100=1 and position() &gt;
100]">
	<table cellpadding="0" cellspacing="0" border="0">
	<xsl:apply-templates
select=".|following-sibling::node[valid = 1 and position() &lt; 100]" />
	</table>
</xsl:for-each>

<xsl:for-each select="node[not(valid = 1) and position() mod 100=1 and position() &gt;
100]">
	<table cellpadding="0" cellspacing="0" border="0">
	<xsl:apply-templates
select=".|following-sibling::node[not(valid = 1) and position() &lt; 100]" />
	</table>
</xsl:for-each>

<xsl:template match="node[valid=1]">
	<tr>
		<td class="valid">
			<xsl:variable name="number">
				<xsl:number value="count(preceding-sibling::output)+1" level="any"
format="1" />
			</xsl:variable>
			<xsl:value-of select="$number" />.
			<xsl:value-of select="data" />
		</td>
	</tr>
</xsl:template>

<xsl:template match="node[not(valid=1)]">
	<tr>
		<td class="invalid">
			<xsl:variable name="number">
				<xsl:number value="count(preceding-sibling::output)+1" level="any"
format="1" />
			</xsl:variable>
			<xsl:value-of select="$number" />.
			<xsl:value-of select="data" />
		</td>
	</tr>
</xsl:template>

The main difference here is that I'm doing selective template
application rather than sorting.

What I'm discovering, though, is that once all nodes that have a valid
node are output, my current method skips any nodes that are not valid
until the first complete set of 100 (I.e., where mod 100 = 1).

E.g., If I have 150 valid nodes and then 150 invalid nodes, this
stylesheet seems to output all 150 valid nodes, skips 50 invalid nodes
and then prints the final 100 invalid nodes.

I would've thought I could've overcome this by including something along
the lines of:

<xsl:for-each select="node[not(valid = 1) and position() =
count(node[valid = 1]) + 1]">
	<table cellpadding="0" cellspacing="0" border="0">
		<xsl:apply-templates select=".|following-sibling::node[not(valid = 1)
and not(position() mod 100 = 1)]" />
	</table>
</xsl:for-each>

As I understand the grouping, I would've thought this would start with
the node whose position was immediately after the last valid node and
then iterate until it hit a node with mod 100 = 1, but it doesn't seem
to be working.

Is my interpretation of how this grouping strategy works correct?

What can I do to output these interim nodes successfully (and most
appropriately)?

Thanks!

-tfo

> I have some XML that looks like this:
> 
> <data>
> 	<node>
> 		<data>data</data>
> 		<valid>1</valid>
> 	</node>
> 	<node>
> 		<data>data</data>
> 	</node>
> </data>
> 
> There can be any number and sequence of nodes. Some nodes are flagged
> with a validity node.
> 
> I'm trying to transform it to HTML using XSL. Here are the conditions:
> 
> 1. Group it by 100.
> 2. Sort it by whether or not the nodes have a "valid" node (descending
> from valid to non-valid, which is just a binary existence check).
> 3. The output needs to be numbered sequentially.
> 4. The output needs to be formatted differently based on whether a
> "valid" node is encountered.
> 5. The first chunk of the output needs to include a header.
> 
> Basically, I'm breaking it up into 100-row HTML tables.
> 
> Here's my current approach:
> 
> <table>
> 	<th>Header</th>
> <xsl:apply-templates select="node[position() &lt;= 100]">
> 	<xsl:sort select="valid" />
> </xsl:apply-templates>
> </table>
> 
> <xsl:for-each select="node[position() mod 100=1 and position() &gt;
> 100]">
> 	<table cellpadding="0" cellspacing="0" border="0">
> 	<xsl:apply-templates
> select=".|following-sibling::node[position()&lt;100]">
> 		<xsl:sort select="valid" />
> 	</xsl:apply-templates>
> 	</table>
> </xsl:for-each>
> 
> <xsl:template match="node[valid=1]">
> 	<tr>
> 		<td class="valid">
> 			<xsl:variable name="number">
> 				<xsl:number value="count(preceding-sibling::*)+1" level="any"
> format="1" />
> 			</xsl:variable>
> 			<xsl:value-of select="$number" />.
> 			<xsl:value-of select="data" />
> 		</td>
> 	</tr>
> </xsl:template>
> 
> <xsl:template match="node[not(valid=1)]">
> 	<tr>
> 		<td class="invalid">
> 			<xsl:variable name="number">
> 				<xsl:number value="count(preceding-sibling::*)+1" level="any"
> format="1" />
> 			</xsl:variable>
> 			<xsl:value-of select="$number" />.
> 			<xsl:value-of select="data" />
> 		</td>
> 	</tr>
> </xsl:template>
> 
> Unfortunately, the result of this transformation neither sorts the
> output, nor numbers the output sequentially. The grouping works, but I
> was under the impression that the sort would apply to the nodes before
> their positions were considered. What I understand less is why the sort
> does not seem actually to order the nodes correctly in the output.
> 
> Any help is much appreciated.
> 
> Thanks!
> 
> -tfo


Current Thread