Subject: Re: [xsl] Generating a list of items NOT present in source XML From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx> Date: Fri, 17 May 2002 18:57:54 +0100 |
Hi Greg, > I'm wondering if anyone has created a template (or has ideas on how > to efficiently create such) that generates a list of items that is > missing from a given source document. Assume that a node-set is > passed that contains an assorted cluster of indices, the template > should start at one, and continue to the highest provided element, > outputting all missing elements. Hrm... It'd be a lot easier if your nodes were sorted to start with -- are you happy to use a node-set() extension function? If so, sort them: <xsl:variable name="sorted-childnode-rtf"> <xsl:for-each select="obj/childnode"> <xsl:sort select="." data-type="number" /> <xsl:copy-of select="." /> </xsl:for-each> </xsl:variable> <xsl:variable name="sorted-childnodes" select="exsl:node-set($sorted-childnodes)" /> Then have a recursive template that takes a running count and a bunch of nodes as parameters: <xsl:template name="fill-in-gaps"> <xsl:param name="count" select="1" /> <xsl:param name="nodes" select="/.." /> ... </xsl:template> If there aren't any nodes left, you're done. If there are, compare the first node with the count. If it's the same, then add one to the count and move on to the next node: <xsl:template name="fill-in-gaps"> <xsl:param name="count" select="1" /> <xsl:param name="nodes" select="/.." /> <xsl:if test="$nodes"> <xsl:choose> <xsl:when test="$nodes[1] = $count"> <xsl:call-template name="fill-in-gaps"> <xsl:with-param name="count" select="count + 1" /> <xsl:with-param name="nodes" select="$nodes[position() > 1]" /> </xsl:call-template> </xsl:when> <xsl:otherwise> ... </xsl:otherwise> </xsl:choose> </xsl:if> </xsl:template> Otherwise there's a gap, so output the thing to fill it, add one to the count but keep the set of nodes the same for the next call: <xsl:template name="fill-in-gaps"> <xsl:param name="count" select="1" /> <xsl:param name="nodes" select="/.." /> <xsl:if test="$nodes"> <xsl:choose> <xsl:when test="$nodes[1] = $count"> <xsl:call-template name="fill-in-gaps"> <xsl:with-param name="count" select="count + 1" /> <xsl:with-param name="nodes" select="$nodes[position() > 1]" /> </xsl:call-template> </xsl:when> <xsl:otherwise> <obj> <childnode><xsl:value-of select="$count" /></childnode> </obj> <xsl:call-template name="fill-in-gaps"> <xsl:with-param name="count" select="count + 1" /> <xsl:with-param name="nodes" select="$nodes" /> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:if> </xsl:template> If you don't want to use an extension function, then you should apply templates to the nodes in ascending order: <xsl:apply-templates select="obj/childnode"> <xsl:sort select="." data-type="number" /> </xsl:apply-templates> Then have a recursive template that matches the node and extracts from it a value (one less than its value) and a bunch of nodes (those childnode elements that have a value less than it does): <xsl:template match="childnode" name="fill-in-gaps"> <xsl:param name="value" select=". - 1" /> <xsl:param name="nodes" select="../../obj/childnode[. < current()]" /> ... </xsl:template> In the recursive template work down through the values until there's a node in $nodes that has the same value as $value: <xsl:template match="childnode" name="fill-in-gaps"> <xsl:param name="value" select=". - 1" /> <xsl:param name="nodes" select="../../obj/childnode[. < current()]" /> <xsl:if test="not($value = $nodes)"> <obj><childnode><xsl:value-of select="$value" /></childnode></obj> <xsl:call-template name="fill-in-gaps"> <xsl:with-param name="value" select="$value - 1" /> <xsl:with-param name="nodes" select="$nodes" /> </xsl:call-template> </xsl:if> </xsl:template> --- Easiest XSLT 2.0 way, I think, would be to create a sequence of numbers from 1 to the maximum value of the childnodes, then filter it to contain only those values that aren't the same as the values of one of the nodes: <xsl:variable name="nodes" select="obj/childnode" /> <xsl:for-each select="(1 to max($nodes))[not($nodes = .)]"> <obj><childnode><xsl:value-of select="." /></childnode></obj> </xsl:for-each> It's not very efficient, though, especially for lots of nodes. Cheers, Jeni --- Jeni Tennison http://www.jenitennison.com/ XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
Re: [xsl] Generating a list of item, Jeni Tennison | Thread | Re: [xsl] Generating a list of item, Greg Faron |
Re: [xsl] Document() and &, Mike Brown | Date | Re: [xsl] Generating a list of item, Jeni Tennison |
Month |