Re: [xsl] CSV data in attribute - how to get unique values?

Subject: Re: [xsl] CSV data in attribute - how to get unique values?
From: Brandon Ibach <brandon.ibach@xxxxxxxxxxxxxxxxxxx>
Date: Wed, 5 Jan 2011 04:23:42 -0500
Here's a working solution.  It isn't the prettiest, but given the
minimal string processing features in XSLT 1.0, I don't know that
there's a significantly cleaner way to do what you want.

	<xsl:template match="/">
		<!-- List all named bottomlevel items grouped by tokens -
		   - in the "attrib" attribute closest to each item     -->
		<xsl:call-template name="items-by-token">
			<xsl:with-param name="items" select="//bottomlevel[@name]"/>
		</xsl:call-template>
	</xsl:template>

	<xsl:template name="items-by-token">
		<xsl:param name="items" select="/.."/><!-- Items to list -->
		<xsl:param name="tokens" select="''"/><!-- Tokens from current item -->
		<xsl:param name="seen" select="' '"/><!-- Tokens already seen / listed -->
		<xsl:choose>
			<xsl:when test="not($tokens or $items)"/><!-- No remaining tokens
or items, abort -->
			<xsl:when test="not($tokens)"><!-- No remaining tokens for current
item, get next -->
				<xsl:variable name="att"
select="($items[1]/ancestor-or-self::*/@attrib)[last()]"/>
				<xsl:call-template name="items-by-token">
					<xsl:with-param name="items" select="$items[position() &gt; 1]"/>
					<xsl:with-param name="tokens"
select="concat(normalize-space(translate($att, ',', ' ')), ' ')"/>
					<xsl:with-param name="seen" select="$seen"/>
				</xsl:call-template>
			</xsl:when>
			<xsl:otherwise><!-- Process next token -->
				<xsl:variable name="att" select="substring-before($tokens, ' ')"/>
				<xsl:variable name="search" select="concat(' ', $att, ' ')"/>
				<xsl:variable name="new" select="not(contains($seen, $search))"/>
				<xsl:variable name="seen-add">
					<xsl:if test="$new"><xsl:value-of select="concat($att, ' ')"/></xsl:if>
				</xsl:variable>
				<xsl:if test="$new">
					<xsl:value-of select="concat('&#10;Attrib: ', $att, '&#10;')"/>
					<xsl:for-each select="//bottomlevel[@name][contains(concat(' ',
normalize-space(translate((ancestor-or-self::*/@attrib)[last()], ',',
' ')), ' '), $search)]">
						<xsl:value-of select="concat('- ', @name, '&#10;')"/>
					</xsl:for-each>
				</xsl:if>
				<xsl:call-template name="items-by-token">
					<xsl:with-param name="items" select="$items"/>
					<xsl:with-param name="tokens" select="substring-after($tokens, ' ')"/>
					<xsl:with-param name="seen" select="concat($seen, $seen-add)"/>
				</xsl:call-template>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>

-Brandon :)


On Wed, Dec 29, 2010 at 10:25 PM, vesse <vessep@xxxxxxxxx> wrote:
> Hi,
>
> I have an XML document that looks roughly like this:
> <root>
>  <toplevel attrib="123">
>     <midlevel attrib="453,123">
>        <bottomlevel attrib="853,123" name="MyName"/>
>        <bottomlevel name="OtherItem"/>
>
> I need to group the bottomlevel items under each unique attribute ID.
> Attribute on the lowest level found is the one that's important (
> (ancestor-or-self::/@attrib)[last()] ). So in the above example the
> output would be
>
> Attrib:853
> - MyName
>
> Attrib: 123
> - MyName
> - OtherItem
>
> Attrib: 453
> - OtherItem
>
> In the old days the attribute value was not CSV data so I used
>
> <xsl:key name="attribs" match="*" use="@attrib"/>
> <xsl:for-each select="*[generate-id()=generate-id(key('attribs',
@attrib)[1])]">
>
> to go through the items by the attribute values. This does not of
> course produce the wanted output anymore. Is it anyway possible to get
> the unique values from the CSV data to work with, and then be also
> able to find the bottomlevel items that have the current ID? I'm using
> XSLT 1.0 (transformation done in browser). Changing the XML file
> structure is out of the question.
>
>
> BR,
> Vesse

Current Thread