Re: [xsl] Excluding too much in nested for-each

Subject: Re: [xsl] Excluding too much in nested for-each
From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx>
Date: Wed, 21 Apr 2004 13:09:48 +0100
Hi Ragulf,

> I have nothing against key's if that is a solution, I have just
> never worked with key's in XSL, and is therefore not proficient in
> this, but if a solutions with key's is easier, then I will very much
> want to learn.

If I understand what you're doing correctly, the key-based (Muenchian)
method would look something like:

<xsl:key name="ElemsByDate"
         match="Elem[@Type = '2' or @Type = '4']"
         use="@Date" />
         
<xsl:key name="ElemsByDateAndID"
         match="Elem[@Type = '2' or @Type = '4']"
         use="concat(@Date, ':', ElemID)" />
         
<xsl:template match="/">
  <output>
    <!-- select unique Elems by Date -->
    <xsl:for-each select="/root/Elements/Elem
                            [generate-id(key('ElemsByDate', @Date)[1]) =
                             generate-id(.)]">
      <!-- sort by Date -->
      <xsl:sort select="substring(@Date,$YearPos,4)" order="descending"/>
      <xsl:sort select="substring(@Date,$MonthPos,2)" order="descending"/>
      <xsl:sort select="substring(@Date,$DayPos,2)" order="descending"/>
      <OutputByDate>
        <Date><xsl:value-of select="@Date" /></Date>
        <!-- select unique Elems by Date and ID -->
        <xsl:for-each select="key('ElemsByDate', @Date)
                                [generate-id(key('ElemsByDateAndID',
                                                 concat(@Date, ':', ElemID))[1]) =
                                 generate-id(.)]">
          <OutputElem>
            <ID><xsl:value-of select="ElemID" /></ID>
            <xsl:for-each select="key('ElemsByDateAndID',
                                      concat(@Date, ':', ElemID))">
              <OutputElemSub>
                <Type><xsl:value-of select="@Type" /></Type>
                <Name><xsl:value-of select="Name" /></Name>
              </OutputElemSub>
            </xsl:for-each>
          </OutputElem>
        </xsl:for-each>
      </OutputByDate>
    </xsl:for-each>
  </output>
</xsl:template>

Note that the keys do the filtering of the <Elem> elements so that the
groups only contain those whose Type is 2 or 4. You won't get a group
appearing if it only contains <Elem> elements whose Type is 1 or 3.

The XSLT 2.0 version is similar, but of course doesn't require keys,
and it might help you understand the logic of the XSLT 1.0 version
above:

<xsl:template match="/">
  <output>
    <xsl:for-each-group select="/root/Elements/Elem
                                  [@Type = '2' or @Type = '4']"
                        group-by="@Date">
      <xsl:sort select="substring(@Date,$YearPos,4)" order="descending"/>
      <xsl:sort select="substring(@Date,$MonthPos,2)" order="descending"/>
      <xsl:sort select="substring(@Date,$DayPos,2)" order="descending"/>
      <OutputByDate>
        <Date><xsl:value-of select="@Date" /></Date>
        <xsl:for-each-group select="current-group()"
                            group-by="ElemID">
          <OutputElem>
            <ID><xsl:value-of select="ElemID" /></ID>
            <xsl:for-each select="current-group()">
              <OutputElemSub>
                <Type><xsl:value-of select="@Type" /></Type>
                <Name><xsl:value-of select="Name" /></Name>
              </OutputElemSub>
            </xsl:for-each>
          </OutputElem>
        </xsl:for-each-group>
      </OutputByDate>
    </xsl:for-each-group>
  </output>
</xsl:template>

Cheers,

Jeni

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

Current Thread