xsl-list
[Top] [All Lists]

Re: different first element in a list

2003-02-22 15:15:39
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[(_at_)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