Reputation: 4283
I am looping through a collection of nodes, each with two elements, and want to output a two column table for each group. The table is sorted alphabetically, down the first column, then down the second column. I'm grouping on the first element, then outputting each second element in the two columns.
Example data source:
<ArrayOfEIS_CT_AssignmentByRegion
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<EIS_CT_AssignmentByRegion>
<Region>MIDDLE EAST, NORTH AFRICA and EUROPE</Region>
<CountryName>IRAQ</CountryName>
</EIS_CT_AssignmentByRegion>
<EIS_CT_AssignmentByRegion>
<Region>MIDDLE EAST, NORTH AFRICA and EUROPE</Region>
<CountryName>JORDAN</CountryName>
</EIS_CT_AssignmentByRegion>
<EIS_CT_AssignmentByRegion>
<Region>MIDDLE EAST, NORTH AFRICA and EUROPE</Region>
<CountryName>GAZA AND WEST BANK</CountryName>
</EIS_CT_AssignmentByRegion>
<EIS_CT_AssignmentByRegion>
<Region>MIDDLE EAST, NORTH AFRICA and EUROPE</Region>
<CountryName>KUWAIT</CountryName>
</EIS_CT_AssignmentByRegion>
<EIS_CT_AssignmentByRegion>
<Region>SUBSAHARAN AFRICA</Region>
<CountryName>TOGO</CountryName>
</EIS_CT_AssignmentByRegion>
<EIS_CT_AssignmentByRegion>
<Region>SUBSAHARAN AFRICA</Region>
<CountryName>ZIMBABWE</CountryName>
</EIS_CT_AssignmentByRegion>
<EIS_CT_AssignmentByRegion>
<Region>SUBSAHARAN AFRICA</Region>
<CountryName>ZAMBIA</CountryName>
</EIS_CT_AssignmentByRegion>
<EIS_CT_AssignmentByRegion>
<Region>SUBSAHARAN AFRICA</Region>
<CountryName>UGANDA</CountryName>
</EIS_CT_AssignmentByRegion>
</ArrayOfEIS_CT_AssignmentByRegion>
XSLT:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:w="http://schemas.microsoft.com/office/word/2006/wordml">
<xsl:output method="xml" indent="yes" encoding="utf-8"/>
<xsl:key name="list-by-region" match="EIS_CT_AssignmentByRegion" use="concat(generate-id(..), '|', Region)"/>
<xsl:template match="/ArrayOfEIS_CT_AssignmentByRegion">
<html>
<body>
<xsl:for-each select="EIS_CT_AssignmentByRegion[generate-id() = generate-id(key('list-by-region', concat(generate-id(..), '|', Region))[1])]">
<xsl:sort select="Region" />
<xsl:sort select="CountryName" />
<xsl:variable name="halfIndex" select="floor(count(key('list-by-region', concat(generate-id(..), '|', Region))) div 2)" />
<table>
<thead>
<tr>
<th colspan="2">
<xsl:value-of select="Region" />
</th>
</tr>
</thead>
<tbody>
<xsl:for-each select="key('list-by-region', concat(generate-id(..), '|', Region))">
<xsl:sort select="CountryName" />
<xsl:variable name="countryColumn2" select="following-sibling::*[position() = $halfIndex]" />
<xsl:if test="position() <= $halfIndex">
<tr>
<td>
<xsl:value-of select="CountryName" />
</td>
<td>
<xsl:value-of select="$countryColumn2" />
</td>
</tr>
</xsl:if>
</xsl:for-each>
</tbody>
</table>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
The output I want is this:
<html>
<body>
<table>
<thead>
<tr>
<th colspan="2">
MIDDLE EAST, NORTH AFRICA and EUROPE
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
GAZA AND WEST BANK
</td>
<td>
JORDAN
</td>
</tr>
<tr>
<td>
IRAQ
</td>
<td>
KUWAIT
</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th colspan="2">
SUBSAHARAN AFRICA
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
TOGO
</td>
<td>
ZAMBIA
</td>
</tr>
<tr>
<td>
UGANDA
</td>
<td>
ZIMBABWE
</td>
</tr>
</tbody>
</table>
</body>
</html>
My approach is to only process half of each group, and select the country from the second half and output it in the second column. My problem is that for some groups, the wrong country is being selected. Often, a country from another group is being selected.
For testing, I did a run outputting all countries in order, single column, and they are being grouped and sorted correctly. So this line
<xsl:variable name="countryColumn2"
select="following-sibling::*[position() = $halfIndex]" />
for some groups, leaves the current context to select the sibling node. Any ideas? Thanks.
Upvotes: 1
Views: 156
Reputation: 243529
<xsl:variable name="countryColumn2" select="following-sibling::*[position() = $halfIndex]" />
for some groups, leaves the current context to select the sibling node. Any ideas?
Good question, +1.
Sorting with <xsl:sort>
doesn't change the original sibling relationships between the nodes in the XML document.
Therefore, for two nodes: node1
and its following sibling node2,
although node1
comes after node2
as result of the sorting, it is still true that node2
is a following sibling of node1
.
Solution:
Pass1: Output the result of the sorting in a temporary tree and in this tree the sibling relationships between the nodes exactly reflects their sort order.
Pass2: Continue the processing in a second pass on the result of the first pass. Now you can use the sibling axes in the intended way.
Upvotes: 1