Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Idiomatic way to express set equality in XPath 2.0

If $A and $B are sequences, what is the idiomatically preferred way of testing $A and $B for set equality? I know the existential semantics behavior of ($A = $B) makes this expression not the answer. The ordering semantics of deep-equal() prohibit me from using that as well.

My impulse is to use:

((every $a in $A satisfies $a = $B) and (every $b in $B satisfies $b = $A))

I've found surprisingly little about set equality testing via Google (to be precise, I've found absolutely nothing), and I didn't see it mentioned in @Michael-Kay's Chapter 8, 9, 10, or 13. It's difficult to believe I'm the first XPath user to have encountered this need. It makes me wonder if I'm asking the wrong question.

like image 388
Cary Millsap Avatar asked Mar 25 '15 20:03

Cary Millsap


1 Answers

An interesting and well-asked question! In my opinion, using every and satisfies to overcome the existential properties of sequence comparisons is a very valid and canonical enough method.

But since you are asking about another approach: How about sorting the sequences before comparing them with deep-equal()? Let us assume two sequences in the following stylesheet:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output method="text"/>

    <xsl:variable name="A" select="('a','b','c')"/>
    <xsl:variable name="B" select="('a','c','b')"/>

    <xsl:template match="/">
      <xsl:choose>
          <xsl:when test="deep-equal($A,$B)">
              <xsl:text>DEEP-EQUAL!</xsl:text>
          </xsl:when>
          <xsl:otherwise>
              <xsl:text>NOT DEEP-EQUAL</xsl:text>
          </xsl:otherwise>
      </xsl:choose>
    </xsl:template>

</xsl:transform>

And of course, this transformation returns

NOT DEEP-EQUAL

But if you sort the sequences before comparing them, for instance with a custom function that makes use of xsl:perform-sort, see the relevant part of the specification:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
    xmlns:local="www.local.com" extension-element-prefixes="local"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsl:output method="text"/>

    <xsl:variable name="A" select="('a','b','c')"/>
    <xsl:variable name="B" select="('a','c','b')"/>

    <xsl:template match="/">
      <xsl:choose>
          <xsl:when test="deep-equal(local:sort($A),local:sort($B))">
              <xsl:text>DEEP-EQUAL!</xsl:text>
          </xsl:when>
          <xsl:otherwise>
              <xsl:text>NOT DEEP-EQUAL</xsl:text>
          </xsl:otherwise>
      </xsl:choose>
    </xsl:template>

    <xsl:function name="local:sort" as="xs:anyAtomicType*">
        <xsl:param name="in" as="xs:anyAtomicType*"/>
        <xsl:perform-sort select="$in">
            <xsl:sort select="."/>
        </xsl:perform-sort>
    </xsl:function>

</xsl:transform>

then, the result will be

DEEP-EQUAL!

EDIT: In fact, set equality would entail that not only order is irrelevant, but also duplicates should not make a difference. Therefore, proper set equality would mean to also apply distinct-values() to the sequence variables:

<xsl:when test="deep-equal(local:sort(distinct-values($A)),local:sort(distinct-values($B)))">
like image 56
Mathias Müller Avatar answered Oct 06 '22 09:10

Mathias Müller