1

Is there an easy way to get the following code to only show distinct values please?

<xsl:template match="/">
   <xsl:for-each select="NewDataSet/Vehicle">
      <xsl:value-of select="ManufacturerName" />
   </xsl:for-each>

<!-- Does a load of other stuff down here -->

</xsl:template>

This is essentially what the xml looks like:

<NewDataSet>
  <Vehicle>
   <ManufacturerName>FORD</ManufacturerName>
  </Vehicle>
  <Vehicle>
   <ManufacturerName>CHEVROLET</ManufacturerName>
  </Vehicle>
  <Vehicle>
   <ManufacturerName>VAUXHALL</ManufacturerName>
  </Vehicle>
  <Vehicle>
   <ManufacturerName>VAUXHALL</ManufacturerName>
  </Vehicle>
  <Vehicle>
   <ManufacturerName>VAUXHALL</ManufacturerName>
  </Vehicle>
</NewDataSet>

Thanks.

Strontium_99
  • 1,771
  • 6
  • 31
  • 52

3 Answers3

4

To get distinct values, the most efficient way is usually to use a technique called Muenchian Grouping. You may not actually be getting all the vehicles in a group, but to get the distinct values you effectively get the first element in each group.

To do this, you define a key to look-up Vehicle elements by the ManufacturerName

<xsl:key name="Manufacturer" match="Vehicle" use="ManufacturerName" />

Then, to get the distinct values, you iterate over all Vehicle elements, and pick the one which occurs first in the key for its given ManufacturerName

<xsl:for-each select="Vehicle
              [generate-id() = generate-id(key('Manufacturer', ManufacturerName)[1])]">

Try this XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:key name="Manufacturer" match="Vehicle" use="ManufacturerName" />

  <xsl:template match="/*">
    <xsl:for-each select="Vehicle[generate-id() = generate-id(key('Manufacturer', ManufacturerName)[1])]">
      <xsl:value-of select="ManufacturerName"/>
    </xsl:for-each>
    <!-- Does a load of other stuff down here -->
  </xsl:template>
</xsl:stylesheet>
Tim C
  • 70,053
  • 14
  • 74
  • 93
2

This XSLT stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>

  <xsl:template match="NewDataSet">
    <xsl:apply-templates select="Vehicle[not(ManufacturerName=preceding-sibling::Vehicle/ManufacturerName)]"/>  
  </xsl:template>

  <xsl:template match="Vehicle">
    <xsl:value-of select="ManufacturerName"/>
    <xsl:text>&#xa;</xsl:text>
  </xsl:template>
</xsl:stylesheet>

produces the following output when applied to your example XML:

FORD
CHEVROLET
VAUXHALL

The important part is the select in the xsl:apply-templates. It works by only selecting Vehicle elements whose Manufacturer has not appeared in one of the preceding Vehicle elements at the same level.

Ben L
  • 1,302
  • 11
  • 23
  • Sorry, this is totally my fault for over simplifying the code. I'm running into problems, and this is due to my lack of knowledge with regard to XSL. My xslt is doing a load of other stuff, so the distinct list need to be assimilated from within a template. I've again amended my original post to try and describe what I mean. Sorry. – Strontium_99 Sep 11 '13 at 12:08
1

This has to be the easiest way to show distinct values... (The <xsl:text>&#xa;</xsl:text> is just to display new lines).

XSL

<xsl:template match="/">
    <xsl:for-each select="//ManufacturerName[not(.=preceding::*)]">
        <xsl:value-of select="." /><xsl:text>&#xa;</xsl:text>
    </xsl:for-each>
</xsl:template>

Result

FORD
CHEVROLET
VAUXHALL

More information here.

Community
  • 1
  • 1
Nick Grealy
  • 24,216
  • 9
  • 104
  • 119