This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kProdByType"
match="product" use="productType"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/products">
<products>
<xsl:apply-templates select=
"product[generate-id()
=
generate-id(key('kProdByType', productType)[1])
]
"/>
</products>
</xsl:template>
<xsl:template match="product">
<productType value="{productType}">
<xsl:apply-templates mode="copy"
select="key('kProdByType', productType)"/>
</productType>
</xsl:template>
<xsl:template match="product" mode="copy">
<xsl:call-template name="identity"/>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<products>
<product>
<productNumber>1</productNumber>
<productType>TypeA</productType>
</product>
<product>
<productNumber>2</productNumber>
<productType>TypeB</productType>
</product>
<product>
<productNumber>3</productNumber>
<productType>TypeA</productType>
</product>
<product>
<productNumber>4</productNumber>
<productType>TypeC</productType>
</product>
<product>
<productNumber>5</productNumber>
<productType>TypeA</productType>
</product>
</products>
produces the wanted, correct grouping:
<products>
<productType value="TypeA">
<product>
<productNumber>1</productNumber>
<productType>TypeA</productType>
</product>
<product>
<productNumber>3</productNumber>
<productType>TypeA</productType>
</product>
<product>
<productNumber>5</productNumber>
<productType>TypeA</productType>
</product>
</productType>
<productType value="TypeB">
<product>
<productNumber>2</productNumber>
<productType>TypeB</productType>
</product>
</productType>
<productType value="TypeC">
<product>
<productNumber>4</productNumber>
<productType>TypeC</productType>
</product>
</productType>
</products>
Do note: This is an example of the well-known Muenchian method for grouping, which is the fastest known gouping technique in XSLT 1.0.
XSLT 2.0 solution:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/products">
<xsl:for-each-group select="product"
group-by="productType">
<productType value="{productType}">
<xsl:apply-templates select="current-group()"/>
</productType>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
when this XSLT 2.0 transformation is applied on the provided XML document, exactly the same, correctly-grouped result is produced.