Re: [xsl] compare two node sets

Subject: Re: [xsl] compare two node sets
From: "Michael Kay mike@xxxxxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Tue, 21 Jan 2020 17:24:53 -0000
Indeed this suggests a generalization using higher-order functions:

<xsl:function name="has-duplicates" as="xs:boolean">
  <xsl:param name="nodes" as="node()*"/>
  <xsl:param name="hashCode" as="function(node()) as xs:anyAtomicType"/>
  <xsl:param name="equals" as="function(node(), node()) as xs:boolean"/>
  <xsl:variable name="groups" as="map(xs:string, node()*)"
select="map:merge($nodes ! map{$hashCode(.) : .},
map{'duplicates':'combine'})"/>
  <xsl:sequence select="some $k in map:keys($groups) satisfies
                                       ( let $g := $groups($k) return
                                         count($g) gt 1 and some $i in $g, $j
in ($g except $i) satisfies $equals($i, $j) )"/>
</xsl:function>

so you can supply the functions for computing signatures and determining node
equality as parameters.

Michael Kay
Saxonica


> On 21 Jan 2020, at 15:42, Piez, Wendell A. (Fed) wendell.piez@xxxxxxxx
<xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote:
>
> Hi,
>
> Another solution not yet suggested in this thread is to avoid deep-equal()
and rely on a signature function.
>
> So for example,
>
> <xsl:function name="f:signature" as="xs:string">
>  <xsl:param name="who" as="node()"/>
>  <xsl:value-of select="string-join($who/(first, last), ' ')"/>
> </xsl:function>
>
>
> Then your list of author-directors is
//author[f:signature(.)=parent::film/child::director/f:signature(.)]
>
> (Or the other way around.)
>
> Wrap the expression in the filter in not() to retrieve authors who did not
direct, etc.
>
> One advantage of this approach is it's easy to adjust the signature logic to
real-world exigencies.
>
> Cheers, Wendell
>
> -----Original Message-----
> From: Wolfhart Totschnig wolfhart.totschnig@xxxxxxxxxxx
<xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
> Sent: Sunday, January 19, 2020 4:22 PM
> To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
> Subject: Re: [xsl] compare two node sets
>
> Thank you, David, Michael, and Liam for the prompt replies! Michael's
solution seems to be the simplest to implement. I use Saxon 9 HE, so XPath 2.0
should be okay. And, indeed, quadratic performance should not be an issue.
However, Saxon throws the following error:
>
> XPST0003: Unexpected token "every" at start of expression
>
> Is there a typo in the expression? I used the expression as given:
>
> <xsl:when test="count(//director) eq count(//author) and every $d in
//director satisfies some $a in //author satisfies deep-equal($d/*, $a/*)">
>
> To clarify, the context node is the <film> element.
>
> Wolfhart
>
>
> On 19.01.20 17:55, Liam R. E. Quin liam@xxxxxxxxxxxxxxxx wrote:
>> On Sun, 2020-01-19 at 20:37 +0000, Wolfhart Totschnig
>> wolfhart.totschnig@xxxxxxxxxxx wrote:
>>> Hello,
>>>
>>> I have an XSL/XPath problem to which I cannot find the solution. I
>>> have an xml file with data about films, in the following form
>>> (simplified):
>>>
>>> [..]
>>> . By contrast, in the following example the test should return
>>> <false>:
>>>
>>>     <film>
>>>        <title>M</title>
>>>        <director>
>>>           <first>Fritz</first>
>>>           <last>Lang</last>
>>>        </director>
>>>        <author>
>>>           <first>Thea von</first>
>>>           <last>Harbou</last>
>>>        </author>
>>>        <author>
>>>           <first>Fritz</first>
>>>           <last>Lang</last>
>>>        </author>
>>>     </film>
>> Why?
>>
>> As stated,
>>  <xsl:mode on-no-match="shallow-copy" />
>>
>>   <xsl:template match="/">
>>     <xsl:apply-templates select="/films/film[
>>         some $a in author satisfies
>>         (
>>             ($a/first = director/first)
>>             and ($a/last = director/last)
>>         )
>>     ]"/>
>>   </xsl:template>
>>
>> Liam

Current Thread