XPath/XQuery same-key function

Summary

Determines whether two atomic values can coexist as separate keys within a map.

Signature

op:same-key(
$k1 as xs:anyAtomicType,
$k2 as xs:anyAtomicType
) as xs:boolean

Properties

This function is deterministic, context-independent, and focus-independent.

Rules

The internal function op:same-key (which is not available at the user level) is used to assess whether two atomic values are considered to be duplicates when used as keys in a map. A map cannot contain two separate entries whose keys are the same as defined by this function. The function is also used when matching keys in functions such as map:get and map:remove.

The function returns true if and only if one of the following conditions is true:

  1. All of the following conditions are true:

    $k1 is an instance of xs:string, xs:anyURI, or xs:untypedAtomic

    $k2 is an instance of xs:string, xs:anyURI, or xs:untypedAtomic

    fn:codepoint-equal($k1, $k2)

    Strings are compared without any dependency on collations.

  2. All of the following conditions are true:

    $k1 is an instance of xs:decimal, xs:double, or xs:float

    $k2 is an instance of xs:decimal, xs:double, or xs:float

    One of the following conditions is true:

    Both $k1 and $k2 are NaN

    xs:double('NaN') is the same key as xs:float('NaN')

    Both $k1 and $k2 are positive infinity

    xs:double('INF') is the same key as xs:float('INF')

    Both $k1 and $k2 are negative infinity

    xs:double('-INF') is the same key as xs:float('-INF')

    $k1 and $k2 when converted to decimal numbers with no rounding or loss of precision are mathematically equal.

    Every instance of xs:double, xs:float, and xs:decimal can be represented exactly as a decimal number provided enough digits are available both before and after the decimal point. Unlike the eq relation, which converts both operands to xs:double values, possibly losing precision in the process, this comparison is transitive.

    Positive and negative zero are the same key.

  3. All of the following conditions are true:

    $k1 is an instance of xs:date, xs:time, xs:dateTime, xs:gYear, xs:gYearMonth, xs:gMonth, xs:gMonthDay, or xs:gDay

    $k2 is an instance of xs:date, xs:time, xs:dateTime, xs:gYear, xs:gYearMonth, xs:gMonth, xs:gMonthDay, or xs:gDay

    One of the following conditions is true:

    Both $k1 and $k2 have a timezone

    Neither $k1 nor $k2 has a timezone

    fn:deep-equal($k1, $k2)

    The use of deep-equal rather than eq ensures that comparing values of different types yields false rather than an error.

    Unlike the eq operator, this comparison has no dependency on the implicit timezone, which means that the question of whether or not a map contains duplicate keys is not dependent on this aspect of the dynamic context.

  4. All of the following conditions are true:

    $k1 is an instance of xs:boolean, xs:hexBinary, xs:base64Binary, xs:duration, xs:QName, or xs:NOTATION

    $k2 is an instance of xs:boolean, xs:hexBinary, xs:base64Binary, xs:duration, xs:QName, or xs:NOTATION

    fn:deep-equal($k1, $k2)

    The use of deep-equal rather than eq ensures that comparing values of different types yields false rather than an error.

Notes

The rules for comparing keys in a map are chosen to ensure that the comparison is:

Context-free: there is no dependency on the static or dynamic context Error-free: any two atomic values can be compared, and the result is either true or false, never an error Transitive: if A is the same key as B, and B is the same key as C, then A is the same key as C.

As always, any algorithm that delivers the right result is acceptable. For example, when testing whether an xs:double value D is the same key as an xs:decimal value that has N significant digits, it is not necessary to know all the digits in the decimal expansion of D to establish the result: computing the first N+1 significant digits (or indeed, simply knowing that there are more than N significant digits) is sufficient.