Re: [xsl] Tricky inclusion match

Subject: Re: [xsl] Tricky inclusion match
From: Karl Stubsjoen <kstubs@xxxxxxxxx>
Date: Thu, 31 Mar 2005 07:40:59 -0700
Thanks Dimitre for another approach.  2 questions:

1)  if a picture contains the following colors: red, red, maroon where
red is the only color which qualifies as a match, will your test catch
this?  In otherwords, this picture does not qualify.

2) xxx:node-set() - what does this mean, what are you refering to?

Something I've heard a lot of on this list is the occasional term
"efficiency".  Is there a list of things to NOT do, recommended
practices, etc in regards to developing an efficient stylesheet?

Karl


On Thu, 31 Mar 2005 15:49:25 +1000, Dimitre Novatchev
<dnovatchev@xxxxxxxxx> wrote:
> On Tue, 29 Mar 2005 17:27:18 +0000, Aron Bock <aronbock@xxxxxxxxxxx> wrote:
> > 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:
> 
> Seems a little bit too long.
> 
> This transformation:
> 
> <xsl:stylesheet version="1.0"
> xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
> 
> <xsl:output omit-xml-declaration="yes"/>
> 
> <xsl:variable name="vColors" select="/*/colors"/>
> 
>  <xsl:template match="/">
>    <xsl:for-each select="/*/*/picture">
>      <xsl:if test="count($vColors/*[. = current()/color]) >= 2">
>        <xsl:value-of select="concat('Picture Id=', @sample, '&#xA;')"/>
>      </xsl:if>
>    </xsl:for-each>
>  </xsl:template>
> </xsl:stylesheet>
> 
> when performed against the originally posted source xml document,
> produces the wanted result:
> 
> Picture Id=2
> Picture Id=4
> Picture Id=5
> 
> This is really simple. No xxx:node-set() has been used.
> 
> Cheers,
> Dimitre Novatchev
> 
> 
> >
> > 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
> > >
> >
> > _________________________________________________________________
> > Don't just search. Find. Check out the new MSN Search!
> > http://search.msn.click-url.com/go/onm00200636ave/direct/01/

Current Thread