Exclude duplicate nodes from when using the xsl:number

Viewed 64

When using the Numbering function it actually gets the node position(). How to exclude items appearing more than once in the xml, from the numbering procedure ? This is My simple xml demo:

<?xml-stylesheet type="text/xsl" href="numbers.xsl"?>
<cars>
  <manufacturer name="Chevrolet">
    <car>Cavalier</car>
    <car>Cavalier</car>
    <car>Corvette</car>
    <car>Impala</car>
  </manufacturer>
  <manufacturer name="Ford">
    <car>Pinto</car>
    <car>Pinto</car>
    <car>Mustang</car>
    <car>Taurus</car>
  </manufacturer> 
</cars>

This is My xsl:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>
 <xsl:template match="/">
  <xsl:text>Automobile manufacturers and their cars&#xA;</xsl:text>
  <xsl:for-each select="cars/manufacturer">
   <xsl:text> Table_</xsl:text>
   <xsl:number count="manufacturer" format="A. ("/>
   <xsl:value-of select="@name"/>
   <xsl:text>)&#xA;</xsl:text>
    <xsl:for-each select="car">
     <xsl:if test="not(preceding::*[car = current()/car])">
      <xsl:number count="car" level="any" format="  1. "/>
      <xsl:value-of select="."/>
      <xsl:text>&#xA;</xsl:text>
     </xsl:if>
    </xsl:for-each>
   </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

This is the desired output:

Table_A. (Chevrolet)

  1. Cavalier
  2. Corvette
  3. Impala

Table_B. (Ford)

  1. Pinto
  2. Mustang
  3. Taurus

Thanks in advance

1 Answers

First thing, in order to exclude duplicate values, you should be using Muenchian grouping, not the inefficient method of comparing each car to all preceding cars (or rather all preceding sibling cars, in case two manufacturers have a similarly named model).

Next, xsl:number looks at the input tree - so if you want the numbering to follow the output, you will need to use a variable to hold the interim result. Try something along the lines of:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="text" encoding="UTF-8"/>

<xsl:key name="car" match="car" use="concat(., '|', ../@name)" />

<xsl:template match="/cars">
    <xsl:variable name="temp">
        <xsl:for-each select="manufacturer">
            <manufacturer name="{@name}">
                <!-- copy only distinct values -->
                <xsl:copy-of select="car[count(. | key('car', concat(., '|', ../@name))[1]) = 1]"/>
            </manufacturer>
        </xsl:for-each>
    </xsl:variable>
    <!-- output -->
    <xsl:for-each select="exsl:node-set($temp)/manufacturer">
        <xsl:text> Table_</xsl:text>
        <xsl:number count="manufacturer" format="A. ("/>
        <xsl:value-of select="@name"/>
        <xsl:text>)&#xA;</xsl:text>
        <xsl:for-each select="car">
            <xsl:number count="car" level="any" format="  1. "/>
            <xsl:value-of select="."/>
            <xsl:text>&#xA;</xsl:text>
        </xsl:for-each> 
    </xsl:for-each> 
</xsl:template>

</xsl:stylesheet>

Alternatively, if your processor supports it, you could use the EXSLT set:distinct() extension function.

Related