This is the mail archive of the xsl-list@mulberrytech.com mailing list .


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: grouping headers


Hi Jeroen,

> I hope I'm making myself clear, I searched the archives for an
> answer but I don't really know what I'm looking for...

I'm surprised that searching for 'grouping' didn't give you an answer!
:)  This is a typical example of grouping-by-value. The most general
approach is the Muenchian Method. In that method, you first define a
key so that you can quickly access all the nodes that have a
particular value.

The name of the key is anything you want (e.g. people). The match
attribute holds a pattern that matches the nodes that you want to
index, in this case the 'person' elements. The use attribute holds a
path from the matched node (the 'person' element) to the value that
you're using to group them (their 'city' child). The key in your
example looks like:

<xsl:key name="people" match="person" use="city" />

With this key in place, you can find all the people from Amsterdam
with:

  key('people', 'Amsterdam')

So given a particular city, in a $city variable, you can find all the
people in that city and generate a list of them with:

  <xsl:for-each select="key('people', $city)">
    <xsl:value-of select="name" /><br />
  </xsl:for-each>

The trickier thing is to find all the cities, since I assume you don't
know that in advance. If you went through all the person elements,
then you would get all the cities, but you would get some of them
repeated several times. You need to make sure that you only get the
first occurrence of each city.

Take 'Amsterdam' as the example. You want to find the city of the
first person element who has a city of 'Amsterdam'. You know how to
get all the person elements who have a city of 'Amsterdam' using the
key (see above). You can use a predicate on the key to get just the
first one:

  key('people', 'Amsterdam')[1]

You need to look through all the person elements and find those that
are the first returned for their particular city. This involves
comparing two nodes; I find the most intuitive way is to use the
generate-id() function, as follows:

  generate-id() = generate-id(key('people', city)[1])

So to get a list of all the person elements that are the first with a
particular city, use:

  person[generate-id() = generate-id(key('people', city)[1])]

You can use set logic instead, if you prefer:

  person[count(.|key('people', city)[1]) = 1]

So you can iterate over those to get a list of them:

  <xsl:for-each select="person[count(.|key('people', city)[1]) = 1]">
    <xsl:sort select="city" />
    <!-- variable used for clarity -->
    <xsl:variable name="city" select="city" />
    <xsl:value-of select="$city" /><br />
    <xsl:for-each select="key('people', $city)">
      <xsl:value-of select="name" /><br />
    </xsl:for-each>
    <br />
  </xsl:for-each>

I hope that helps,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]