xsl-list
[Top] [All Lists]

RE: Tricky inclusion match

2005-03-29 10:27:18
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) &gt;= 2">
               <xsl:text>picture sample #</xsl:text>
               <xsl:value-of select="@sample"/>
               <xsl:text>&#xa;</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(  ) &gt; 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( ) &gt; 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( ) &gt; 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( ) &gt; 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( ) &gt; 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( ) &gt; 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(_at_)gmail(_dot_)com>
Reply-To: xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com
To: xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com
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

--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--


_________________________________________________________________
Don?t just search. Find. Check out the new MSN Search! http://search.msn.click-url.com/go/onm00200636ave/direct/01/


--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--



<Prev in Thread] Current Thread [Next in Thread>