RE: [xsl] Showing unique rows at multiple levels

Subject: RE: [xsl] Showing unique rows at multiple levels
From: "Geert Josten" <geert.josten@xxxxxxxxxxx>
Date: Thu, 5 Oct 2006 08:12:35 +0200
Hi,

Your expression "//book[(bookshelf =
$bookshelfVar)]/author[not(.=preceding::author)]" is too strict (the
predicate too tolerant). You need to limit the preceding::author part to
match only authors that are on the same bookshelf. Similar thing for the
publisher. :-)

Besides, I would recommend trying to use keys and grouping techniques
like the Muenchian method. Something like:

(at top-level)

<xsl:key name="bookshelfs" match="bookshelf" use="." />
<xsl:key name="authors" match="author" use="concat(../bookshelf, '-',
.)" />

(inside bookshelf template)

<xsl:for-each select="key('bookshelfs', .)[1]">
  ...
</xsl:for-each>

(inside author template)

<xsl:for-each select="key('authors', concat(../bookshelf, '-', .)[1]">
  ...
</xsl:for-each>

Probably a lot quicker if your data file becomes larger. Using // is
usually very expensive in that case..

Kind regards,
Geert

>


Drs. G.P.H. Josten
Consultant



Daidalos BV
Source of Innovation
Hoekeindsehof 1-4
2665  JZ  Bleiswijk
Tel.: +31 (0) 10 850 1200
Fax: +31 (0) 10 850 1199
www.daidalos.nl
KvK 27164984


De informatie - verzonden in of met dit emailbericht - is afkomstig van
Daidalos BV en is uitsluitend bestemd voor de geadresseerde. Indien u dit
bericht onbedoeld hebt ontvangen, verzoeken wij u het te verwijderen. Aan dit
bericht kunnen geen rechten worden ontleend.


> Van: Glen Mazza [mailto:grm7790@xxxxxxxxxxx]
> Verzonden: woensdag 4 oktober 2006 20:08
> Aan: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
> Onderwerp: [xsl] Showing unique rows at multiple levels
>
> Hello, for this input XML:
>
> <books>
>     <book>
>         <title>T1</title>
>         <author>A5</author>
>         <publisher>P7</publisher>
>         <bookshelf>B4</bookshelf>
>     </book>
>     <book>
>         <title>T2</title>
>         <author>A6</author>
>         <publisher>P8</publisher>
>         <bookshelf>B4</bookshelf>
>     </book>
>     <book>
>         <title>T4</title>
>         <author>A5</author>
>         <publisher>P7</publisher>
>         <bookshelf>B6</bookshelf>
>     </book>
>     <book>
>         <title>T3</title>
>         <author>A5</author>
>         <publisher>P7</publisher>
>         <bookshelf>B4</bookshelf>
>     </book>
> </books>
>
> I would like an alphabetically sorted list of each unique
> bookshelf, and under each bookshelf, each unique author of at
> least one book on that shelf, and under each author, each
> unique publisher of at least one book by that author (on that shelf).
>
> I.e., for the above, the following is desired:
>
> B4
> --A5   (show just once even though 2 books of A5)
> ----P7 (show just once even though 2 books of P7 under A5 on B4)
> --A6
> ----P8
> B6
> --A5
> ----P7
>
> Unfortunately, I am getting this:
>
> B4
> --A5
> ----P7
> --A6
> ----P8
> B6
>
> where A5 and P7 are erroneously missing under shelf B6, because A5 and
> P7 already happen to exist under shelf B4.  (In my sample
> data, if I rename A5 to say A15 and P7 to P17, i.e., nodes
> that don't exist elsewhere, these nodes then *will* appear under B6.)
>
> Something is wrong with my filtering--I only want an author
> to be suppressed if it is already listed for the *same*
> shelf, and likewise with publishers under the same author
> (under the same shelf).
>
> Here is my XSLT:
>
> <xsl:stylesheet version="1.0"
>        xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
>
> <xsl:template match="books">
>     <xsl:for-each select="//bookshelf[not(.=preceding::bookshelf)]">
>          <xsl:sort select="."/>
>          <xsl:apply-templates select='.'/>
>     </xsl:for-each>
> </xsl:template>
>
> <xsl:template match="bookshelf">
>      <xsl:variable name="bookshelfVar" select="."/>
>      <xsl:variable name="authorVar" select="../author"/>
>      <xsl:value-of select="."/>
>      <xsl:for-each select="//book[(bookshelf =
> $bookshelfVar)]/author[not(.=preceding::author)]">
>          <xsl:sort select="."/>
>          <xsl:apply-templates select='.'/>
>      </xsl:for-each>
> </xsl:template>
>
> <xsl:template match="author">
>      <xsl:variable name="bookshelfVar" select="../bookshelf"/>
>      <xsl:variable name="authorVar" select="."/>
>      --<xsl:value-of select="."/>
>      <xsl:for-each select="//book[(bookshelf = $bookshelfVar)
> and (author = $authorVar)]/publisher[not(.=preceding::publisher)]">
>          <xsl:sort select="."/>
>          <xsl:apply-templates select='.'/>
>      </xsl:for-each>
> </xsl:template>
>
> <xsl:template match="publisher">
>      ----<xsl:value-of select="."/>
> </xsl:template>
>
> </xsl:stylesheet>
>
> Can anyone see what I am doing wrong?  Any help would be appreciated.
>
> Thanks,
> Glen

Current Thread