- From: Jeni Tennison <jeni@jenitennison.com>
- Date: Sat, 22 Dec 2001 09:58:06 +0000
- To: xsl-editors@w3.org
Hi, The XSLT 2.0 WD does a great job of describing the various types of grouping that might be required, and I really like the flexibility that you get with the current-group() function. However, the current design of xsl:for-each-group reminds me of the old method of sorting (as immortalised in MSXML), where you had a sort-by attribute on xsl:for-each. Taking my lead from the way sorting was eventually handled, I'd like to propose an alternative: using an xsl:group element in a similar way to the way xsl:sort is used, within either xsl:for-each or xsl:apply-templates. I think this alternative would be easier to learn because of its similarity to xsl:sort, and more flexible because it can be used with xsl:apply-templates and because it means you can do several levels of grouping at the same time (though admittedly the latter is not as important as it is with xsl:sort). The details aren't particularly important right now - it's grouping with something like xsl:sort rather than like xsl:for-each that's the main point - but to give a better idea of where I'm coming from... Any xsl:for-each or xsl:apply-templates would contain zero or more xsl:group elements followed by zero or more xsl:sort elements (the xsl:sort elements control the order in which the groups are processed, not their items). The xsl:group element would be empty, with one of two sets of attributes (though perhaps it would be better to have two distinct elements instead): - select - expression - used as the grouping key for the items - collate - yes/no - used to decide whether the grouping is only based on the grouping key for the preceding item (default 'yes', giving the same effect as 'group-by' in xsl:for-each-group) - data-type \ - lang > These attributes as in xsl:sort - collation / - test - used to identify the first or last item in each group - break - before/after - used to indicate whether the node that tests true (defaults to 'before', so the items that test true begin the group) (I think a 'test' (boolean expression) is more general than a 'match' (pattern); in particular it means that you could group sequences of simple-typed values in the same way.) Some examples (as in the WD): First example: <table> <tr> <th>Position</th> <th>Country</th> <th>City List</th> <th>Population</th> </tr> <xsl:for-each select="cities/city"> <xsl:group select="@country" /> <tr> <td><xsl:value-of select="position()"/></td> <td><xsl:value-of select="@country"/></td> <td> <xsl:value-of select="current-group()/@name" separator="," /> </td> <td><xsl:value-of select="sum(current-group()/@pop)" /></td> </tr> </xsl:for-each> </table> or: <xsl:template match="cities"> <table> <tr> <th>Position</th> <th>Country</th> <th>City List</th> <th>Population</th> </tr> <xsl:apply-templates select="cities/city"> <xsl:group select="@country" /> </xsl:apply-templates> </table> </xsl:template> <xsl:template match="city"> <tr> <td><xsl:value-of select="position()"/></td> <td><xsl:value-of select="@country"/></td> <td> <xsl:value-of select="current-group()/@name" separator="," /> </td> <td><xsl:value-of select="sum(current-group()/@pop)" /></td> </tr> </xsl:template> Second example: <xsl:for-each select="cities/city"> <xsl:group select="substring(@name,1,1)"> <xsl:sort select="substring(@name,1,1)"/> <h2> <xsl:value-of select="upper-case(substring(@name,1,1))"/> <xsl:text> (</xsl:text> <xsl:value-of select="count(current-group())"/> <xsl:text>)</xsl:text> </h2> <xsl:for-each select="current-group()"> <p><xsl:value-of select="@name"/></p> </xsl:for-each> </xsl:for-each> (and again you could do it through xsl:apply-templates) Third example: <xsl:template match="body"> <chapter> <xsl:apply-templates select="*"> <xsl:group test="self::h2" /> </xsl:apply-templates> </chapter> </xsl:template> <xsl:template match="h2"> <section title="{.}"> <xsl:apply-templates select="sublist(current-group(), 2)" /> </section> </xsl:template> <xsl:template match="p"> <para><xsl:value-of select="." /></para> </xsl:template> Fourth example: <xsl:template match="p"> <xsl:apply-templates> <xsl:group select="self::ul or self::ol" collate="no" /> </xsl:apply-templates> </xsl:template> <xsl:template match="ul | ol"> <xsl:copy-of select="current-group()" /> </xsl:template> <xsl:template match="node()"> <p><xsl:copy-of select="current-group()" /></p> </xsl:template> We commonly exhort people not to use xsl:for-each, especially with document-oriented XML structures; I think that especially the latter two examples illustrate how much more in keeping with the template style of stylesheet design an xsl:group element would be compared to xsl:for-each-group. Cheers, Jeni --- Jeni Tennison http://www.jenitennison.com/
Received on Sunday, 23 December 2001 07:40:11 UTC