Re: [xsl] different first element in a list

Subject: Re: [xsl] different first element in a list
From: Mike Brown <mike@xxxxxxxx>
Date: Sat, 22 Feb 2003 15:15:39 -0700 (MST)
Lorenzo De Tomasi wrote:
> I have an Xml list, for esample this list of skills:
> 
> <skills>
>     <skill mark="excellent" name="excellentskill">
>     <skill mark="excellent" name="excellent skill">
>     <skill mark="good" name="goodskill">
>     <skill mark="good" name="goodskill">
>     <skill mark="basic" name="basicskill">
>     <skill mark="basic" name="basicskill">
>     <skill mark="excellent" name="excellentskill">
>     <skill mark="good" name="goodskill">
>     <skill mark="basic" name="basicskill">
> </skills>
> 
> and I want to list in groups based on 'mark' attribute:
> [...]
> What I want to obtain is an xhtml list like this
> 
> excellent skills:   excellentskill
>                     excellentskill
>                     excellentskill
> good skills:        goodskill
>                     goodskill
>                     goodskill
> basic skills:       basicskill
>                     basicskill
>                     basicskill
> 
> using <div>s or <table>s

Grouping problems are a FAQ. The most efficient solution is explained at
http://www.jenitennison.com/xslt/grouping/muenchian.html, and in your
case would look something like this:

  <xsl:key name="skills-by-mark" match="skill" use="@mark"/>
  <xsl:template match="skills">
    <table>
      <!-- process a set consisting of the first skill element for each mark -->
      <xsl:for-each select="skill[count(.|key('skills-by-mark',@mark)[1])=1]">
        <tr>
          <td><b><xsl:value-of select="concat(@mark,' skills:')"/></b></td>
          <td>
            <!-- process all skill elements having the current skill's mark -->
            <xsl:for-each select="key('skills-by-mark',@mark)">
              <xsl:value-of select="@name"/>                
              <xsl:if test="position()!=last()"><br/></xsl:if>
            </xsl:for-each>
          </td>
        </tr>
      </xsl:for-each>
    </table>
  </xsl:template>

There is an easier to understand, but less efficient, way that doesn't use
keys, but does the same thing, first identifying the set of skill elements
that are the first ones with that mark, and then for each of those,
finding the rest with that mark.

<xsl:for-each select="skill[not(@mark=preceding-sibling::skill/@mark)]">
  ...
  <xsl:for-each select=".|following-sibling::skill[@mark=current()/@mark]">
    ...


Mike

-- 
  Mike J. Brown   |  http://skew.org/~mike/resume/
  Denver, CO, USA |  http://skew.org/xml/

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


Current Thread