Re: [xsl] Tricky inclusion match

Subject: Re: [xsl] Tricky inclusion match
From: Dimitre Novatchev <dnovatchev@xxxxxxxxx>
Date: Fri, 1 Apr 2005 06:16:40 +1000
On Thu, 31 Mar 2005 07:40:59 -0700, Karl Stubsjoen <kstubs@xxxxxxxxx> wrote:
> 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.

Yes, as the picture id=6 from your original source xml document was
not selected, too. Just try it and also try to understand what the
XPath expression specifies.

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

I saw in the long thread that somebody was discussing solutions that
requires the xxx:node-set() extension function. In my solution there
isn't a (need to) reference to any extension function.

"xxx" is an abbreviation to a prefix bound to the corresponding
vendor-defined namespace in which the particular implementation of the
node-set() extension function is defined.

> 
> 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?

There must be something in Dave's FAQ.

I remember 2-3 very nice entries by Michael Kay on this subject.


Cheers,
Dimitre.

> 
> 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