XPATH equivalent of SELECT DISTINCT

  • Thread starter andrewmrichards
  • Start date
A

andrewmrichards

Hi all

I have a document which looks like this:

<catalog>
<item>
<label>Label 1</label>
<title>Title 1</title>
<artist>Artist 1</artist>
<format>cd</format>
</item>
<item>
<label>Label 1</label>
<title>Title 1</title>
<artist>Artist 1</artist>
<format>download</format>
</item>
<item>
<label>Label 1</label>
<title>Title 2</title>
<artist>Artist 2</artist>
<format>cd</format>
</item>
<item>
<label>Label 2</label>
<title>Title 3</title>
<artist>Artist 3</artist>
<format>cd</format>
</item>
</catalog>

What I'd like to do is use an XSL stylesheet to transform this into:

<catalog>
<label>
<labelName>Label 1</labelName>
<items>
<item>
Title 1 CD
</item>
<item>
Title 1 Download
</item>
Title 2 CD
</item
</items>
</label>
<label>
<labelName>Label 2</labelName>
<items>
<item>
Title 3 CD
</item>
</items>
</label>
</catalog>

I've tried loads of approaches, generally centered around using
position()=1 as a test, or variations of this, and they don't work - I
always end up with everything repeated for EVERY item, and I can't get
a structure where there's only one <label> structure for each label,
no matter how many items they have. I can see why they don't work -
but I can't find an approach which will.

What I want is something akin to <xsl:for-each
select="distint(label)"> in the way that the SQL SELECT DISTINCT
keyword works.

Can anyone point me in the right direction?

Many thanks

Andrew Richards
 
P

Piet van Oostrum

andrewmrichards said:
Hi all

I have a document which looks like this:

<catalog>
<item>
<label>Label 1</label>
<title>Title 1</title>
<artist>Artist 1</artist>
<format>cd</format>
</item>
<item>
<label>Label 1</label>
<title>Title 1</title>
<artist>Artist 1</artist>
<format>download</format>
</item>
<item>
<label>Label 1</label>
<title>Title 2</title>
<artist>Artist 2</artist>
<format>cd</format>
</item>
<item>
<label>Label 2</label>
<title>Title 3</title>
<artist>Artist 3</artist>
<format>cd</format>
</item>
</catalog>

What I'd like to do is use an XSL stylesheet to transform this into:

<catalog>
<label>
<labelName>Label 1</labelName>
<items>
<item>
Title 1 CD
</item>
<item>
Title 1 Download
</item>
Title 2 CD
</item
</items>
</label>
<label>
<labelName>Label 2</labelName>
<items>
<item>
Title 3 CD
</item>
</items>
</label>
</catalog>

I've tried loads of approaches, generally centered around using
position()=1 as a test, or variations of this, and they don't work - I
always end up with everything repeated for EVERY item, and I can't get
a structure where there's only one <label> structure for each label,
no matter how many items they have. I can see why they don't work -
but I can't find an approach which will.

What I want is something akin to <xsl:for-each
select="distint(label)"> in the way that the SQL SELECT DISTINCT
keyword works.

Can anyone point me in the right direction?
In XSLT/XPath 2.0 there is a distinct-values function. But you probably would use for-each-group to collect the items that belong to the same label. In XSLT/XPath 1.0 you could use the Muench method with a key on the labels.
 
M

Martin Honnen

andrewmrichards said:
<catalog>
<item>
<label>Label 1</label>
<title>Title 1</title>
<artist>Artist 1</artist>
<format>cd</format>
</item>
<item>
<label>Label 1</label>
<title>Title 1</title>
<artist>Artist 1</artist>
<format>download</format>
</item>
<item>
<label>Label 1</label>
<title>Title 2</title>
<artist>Artist 2</artist>
<format>cd</format>
</item>
<item>
<label>Label 2</label>
<title>Title 3</title>
<artist>Artist 3</artist>
<format>cd</format>
</item>
</catalog>

What I'd like to do is use an XSL stylesheet to transform this into:

<catalog>
<label>
<labelName>Label 1</labelName>
<items>
<item>
Title 1 CD
</item>
<item>
Title 1 Download
</item>
Title 2 CD
</item
</items>
</label>
<label>
<labelName>Label 2</labelName>
<items>
<item>
Title 3 CD
</item>
</items>
</label>
</catalog>

With XSLT 2.0 (as implemented by Saxon 9 http://saxon.sourceforge.net/,
AltovaXML Tools http://www.altova.com/altovaxml.html, XQSharp
http://www.xqsharp.com/) you can use for-each-group select="item"
group-by="label" as follows:

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

<xsl:strip-space elements="*"/>
<xsl:eek:utput method="xml" indent="yes"/>

<xsl:template match="catalog">
<xsl:copy>
<xsl:for-each-group select="item" group-by="label">
<label>
<labelName><xsl:value-of
select="current-grouping-key()"/></labelName>
<items>
<xsl:apply-templates select="current-group()"/>
</items>
</label>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>

<xsl:template match="item">
<xsl:copy><xsl:value-of select="title, format"/></xsl:copy>
</xsl:template>

</xsl:stylesheet>

With XSLT 1.0 you can use Muenchian grouping
http://www.jenitennison.com/xslt/grouping/index.xml.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top