Karl, here's an XSLT 1.0 solution:
My basis for this are templates from Sal Mangano's "XSLT Cookbook"; please
search this list for other times I've referenced this resource, including
mentioning from where to download its code.
My approach is to hold the <colors> element in a variable, and then to
compare its children against those of each <picture> element, in an
"intersection" operation. If we have 2 or more intersections, print:
Regards,
--A
colors.xsl
======
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:vset="http:/www.ora.com/XSLTCookbook/namespaces/vset"
extension-element-prefixes="vset">
<xsl:import href="vset.ops.xsl"/>
<xsl:output method="text" />
<xsl:template match="/">
<xsl:variable name="colorlist" select="/data/colors"/>
<xsl:for-each select="/data/pictures/picture">
<xsl:variable name="matchcount">
<xsl:call-template name="compare">
<xsl:with-param name="node1" select="$colorlist"/>
<xsl:with-param name="node2" select="."/>
</xsl:call-template>
</xsl:variable>
<xsl:if test="string-length($matchcount) >= 2">
<xsl:text>picture sample #</xsl:text>
<xsl:value-of select="@sample"/>
<xsl:text>
</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template name="compare">
<xsl:param name="node1"/>
<xsl:param name="node2"/>
<xsl:call-template name="vset:intersection">
<xsl:with-param name="nodes1" select="$node1//*"/>
<xsl:with-param name="nodes2" select="$node2//*"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="*" mode="vset:intersection">
<xsl:value-of select="'*'"/>
</xsl:template>
<xsl:template match="node( ) | @*" mode="vset:element-equality">
<xsl:param name="other"/>
<xsl:if test="local-name(.) = local-name($other) and ./text() =
$other/text()">
<xsl:value-of select="true()"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
vset.ops.xsl
======
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:vset="http:/www.ora.com/XSLTCookbook/namespaces/vset">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<!-- The default implementation of element equality. Override in the
importing
stylesheet as neccessary. -->
<xsl:template match="node( ) | @*" mode="vset:element-equality">
<xsl:param name="other"/>
<xsl:if test=". = $other">
<xsl:value-of select="true( )"/>
</xsl:if>
</xsl:template>
<!-- The default set membership test uses element equality. You will rarely
need to
override this in the importing stylesheet. -->
<xsl:template match="node( ) | @*" mode="vset:member-of">
<xsl:param name="elem"/>
<xsl:variable name="member-of">
<xsl:for-each select=".">
<xsl:apply-templates select="." mode="vset:element-equality">
<xsl:with-param name="other" select="$elem"/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="string($member-of)"/>
</xsl:template>
<!-- Compute the union of two sets using "by value" equality. -->
<xsl:template name="vset:union">
<xsl:param name="nodes1" select="/.." />
<xsl:param name="nodes2" select="/.." />
<!-- for internal use -->
<xsl:param name="nodes" select="$nodes1 | $nodes2" />
<xsl:param name="union" select="/.." />
<xsl:choose>
<xsl:when test="$nodes">
<xsl:variable name="test">
<xsl:apply-templates select="$union" mode="vset:member-of">
<xsl:with-param name="elem" select="$nodes[1]" />
</xsl:apply-templates>
</xsl:variable>
<xsl:call-template name="vset:union">
<xsl:with-param name="nodes" select="$nodes[position( ) > 1]" />
<xsl:with-param name="union" select="$union |
$nodes[1][not(string($test))]" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="$union" mode="vset:union" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Return a copy of union by default. Override in importing stylesheet to
recieve
reults as a "callback"-->
<xsl:template match="/ | node( ) | @*" mode="vset:union">
<xsl:copy-of select="."/>
</xsl:template>
<!-- Compute the intersection of two sets using "by value" equality. -->
<xsl:template name="vset:intersection">
<xsl:param name="nodes1" select="/.."/>
<xsl:param name="nodes2" select="/.."/>
<!-- For internal use -->
<xsl:param name="intersect" select="/.."/>
<xsl:choose>
<xsl:when test="not($nodes1)">
<xsl:apply-templates select="$intersect" mode="vset:intersection"/>
</xsl:when>
<xsl:when test="not($nodes2)">
<xsl:apply-templates select="$intersect" mode="vset:intersection"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="test1">
<xsl:apply-templates select="$nodes2" mode="vset:member-of">
<xsl:with-param name="elem" select="$nodes1[1]"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:variable name="test2">
<xsl:apply-templates select="$intersect" mode="vset:member-of">
<xsl:with-param name="elem" select="$nodes1[1]"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:choose>
<xsl:when test="string($test1) and not(string($test2))">
<xsl:call-template name="vset:intersection">
<xsl:with-param name="nodes1" select="$nodes1[position( ) >
1]"/>
<xsl:with-param name="nodes2" select="$nodes2"/>
<xsl:with-param name="intersect" select="$intersect |
$nodes1[1]"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="vset:intersection">
<xsl:with-param name="nodes1" select="$nodes1[position( ) >
1]"/>
<xsl:with-param name="nodes2" select="$nodes2"/>
<xsl:with-param name="intersect" select="$intersect"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Return a copy of intersection by default. Override in importing
stylesheet to
recieve results as a "callback"-->
<xsl:template match="/ | node( ) | @*" mode="vset:intersection">
<xsl:copy-of select="."/>
</xsl:template>
<!-- Compute the differnce between two sets (node1 - nodes2) using "by
value" equality. -->
<xsl:template name="vset:difference">
<xsl:param name="nodes1" select="/.."/>
<xsl:param name="nodes2" select="/.."/>
<!-- For internal use -->
<xsl:param name="difference" select="/.."/>
<xsl:choose>
<xsl:when test="not($nodes1)">
<xsl:apply-templates select="$difference" mode="vset:difference"/>
</xsl:when>
<xsl:when test="not($nodes2)">
<xsl:apply-templates select="$nodes1" mode="vset:difference"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="test1">
<xsl:apply-templates select="$nodes2" mode="vset:member-of">
<xsl:with-param name="elem" select="$nodes1[1]"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:variable name="test2">
<xsl:apply-templates select="$difference" mode="vset:member-of">
<xsl:with-param name="elem" select="$nodes1[1]"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:choose>
<xsl:when test="string($test1) or string($test2)">
<xsl:call-template name="vset:difference">
<xsl:with-param name="nodes1" select="$nodes1[position( ) >
1]"/>
<xsl:with-param name="nodes2" select="$nodes2"/>
<xsl:with-param name="difference" select="$difference"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="vset:difference">
<xsl:with-param name="nodes1" select="$nodes1[position( ) >
1]"/>
<xsl:with-param name="nodes2" select="$nodes2"/>
<xsl:with-param name="difference" select="$difference |
$nodes1[1]"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--
Computes the differnce between two sets (node1 - nodes2) using "by value"
equality.
"Short-circuit"-s processing at the first difference-node.
-->
<xsl:template name="vset:difference-short-circuit">
<xsl:param name="nodes1" select="/.."/>
<xsl:param name="nodes2" select="/.."/>
<!-- For internal use -->
<xsl:param name="difference" select="/.."/>
<xsl:choose>
<xsl:when test="not($nodes1)">
<xsl:apply-templates select="$difference" mode="vset:difference"/>
</xsl:when>
<xsl:when test="not($nodes2)">
<xsl:apply-templates select="$nodes1" mode="vset:difference"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="test1">
<xsl:apply-templates select="$nodes2" mode="vset:member-of">
<xsl:with-param name="elem" select="$nodes1[1]"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:variable name="test2">
<xsl:apply-templates select="$difference" mode="vset:member-of">
<xsl:with-param name="elem" select="$nodes1[1]"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:choose>
<xsl:when test="string($test1) or string($test2)">
<xsl:call-template name="vset:difference-short-circuit">
<xsl:with-param name="nodes1" select="$nodes1[position( ) >
1]"/>
<xsl:with-param name="nodes2" select="$nodes2"/>
<xsl:with-param name="difference" select="$difference"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="vset:difference-short-circuit">
<xsl:with-param name="nodes1" select="/.."/>
<xsl:with-param name="nodes2" select="$nodes2"/>
<xsl:with-param name="difference" select="$difference |
$nodes1[1]"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Return a copy of difference by default. Override in importing
stylesheet to
recieve results as a "callback"-->
<xsl:template match="/ | node( ) | @*" mode="vset:difference">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
From: Karl Stubsjoen <kstubs@xxxxxxxxx>
Reply-To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
Subject: [xsl] Tricky inclusion match
Date: Tue, 29 Mar 2005 07:55:43 -0700
Okay,
Lets say you have a set of colors and you have a whole bunch of
pictures and you want to match on all pictures who have one or more of
the given colors but you have to at least match on 2 of them (unique,
so not red and red - so a picture could list red twice but that would
not be a match). I'm trying to use keys and grouping to solve this
but thinking that I might be making this more difficult. At any rate,
I'm stuck and would appreciate some help : )
Sample Data (expected results below):
<data>
<colors>
<color>red</color>
<color>blue</color>
<color>fucia</color>
<color>violet</color>
</colors>
<pictures>
<picture sample="1">
<color>black</color>
<color>grey</color>
<color>white</color>
</picture>
<picture sample="2">
<color>red</color>
<color>green</color>
<color>brown</color>
<color>blue</color>
</picture>
<picture sample="3">
<color>purple</color>
<color>orange</color>
</picture>
<picture sample="4">
<color>blue</color>
<color>green</color>
<color>red</color>
</picture>
<picture sample="5">
<color>fucia</color>
<color>green</color>
<color>violet</color>
</picture>
<picture sample="6">
<color>red</color>
<color>brown</color>
<color>red</color>
</picture>
</pictures>
</data>
Expected Results (picture matches based on 2 or more colors used and
listed in colors above)
picture sample #2
picture sample #4
picture sample #5
_________________________________________________________________
Dont just search. Find. Check out the new MSN Search!
http://search.msn.click-url.com/go/onm00200636ave/direct/01/