Skip to content

Commit 74e5b03

Browse files
committed
map:put may use either the new or the old key
1 parent b9c4e40 commit 74e5b03

File tree

6 files changed

+84
-65
lines changed

6 files changed

+84
-65
lines changed

specifications/xpath-functions-40/src/function-catalog.xml

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23058,8 +23058,9 @@ xs:QName('xs:double')</eg></fos:result>
2305823058
<item><p>The position of that entry in the <xtermref spec="DM40"
2305923059
ref="dt-entry-order"/> of the result map will correspond to the
2306023060
position of the first of the duplicates.</p></item>
23061-
<item><p>The key of that entry will be the key used
23062-
in the <emph>last</emph> of the duplicates. (Keys may be
23061+
<item><p>The key of the combined entry
23062+
will correspond to the key of one of the duplicates: it is
23063+
<termref def="implementation-dependent"/> which one is chosen. (Keys may be
2306323064
duplicates even though they differ: for example, they may have
2306423065
different type annotations, or they may be <code>xs:dateTime</code>
2306523066
values in different timezones.)</p></item>
@@ -23072,7 +23073,7 @@ xs:QName('xs:double')</eg></fos:result>
2307223073
</fos:rules>
2307323074
<fos:equivalent style="xpath-expression">
2307423075
map:of-pairs($maps =!> map:pairs(),
23075-
$options[exists((?duplicates, ?combine))]
23076+
$options[exists(?duplicates)]
2307623077
otherwise { "duplicates": "use-first" });
2307723078
</fos:equivalent>
2307823079

@@ -23224,7 +23225,7 @@ map:of-pairs($maps =!> map:pairs(),
2322423225
<fos:rules>
2322523226

2322623227
<p>The function <function>map:of-pairs</function>
23227-
<phrase>returns a map</phrase> that
23228+
<phrase>returns a map</phrase> which
2322823229
is formed by combining <termref def="dt-key-value-pair-map">key-value pair maps</termref> supplied in the
2322923230
<code>$input</code>
2323023231
argument.</p>
@@ -23353,8 +23354,10 @@ return fold-left( $input, {},
2335323354
of the result map, the position of the entry containing the result
2335423355
of combining a set of entries with duplicate keys corresponds to
2335523356
the position of the first of the duplicates in the input sequence.</p></item>
23356-
<item><p>The key of the entry containing the combined value is the <emph>last</emph> of
23357-
the several duplicates. (Keys may be duplicates even though they differ:
23357+
<item><p>The key of the combined entry
23358+
will correspond to the key of one of the duplicates: it is
23359+
<termref def="implementation-dependent"/> which one is chosen.
23360+
(Keys may be duplicates even though they differ:
2335823361
for example they may have different type annotations, or they might be
2335923362
<code>xs:dateTime</code> values in different timezones.)</p></item>
2336023363
</ulist>
@@ -24130,20 +24133,18 @@ declare function map:find($input as item()*,
2413024133
any existing entry for the same key.</p>
2413124134
</fos:summary>
2413224135
<fos:rules>
24133-
<p>The function <function>map:put</function> returns <phrase>a <termref def="dt-map"
24134-
>map</termref> that</phrase> contains all entries from the supplied <code>$map</code>,
24135-
with the exception of any entry whose key is the <termref
24136-
def="dt-same-key"
24137-
>same key</termref> as <code>$key</code>, together with a new
24138-
entry whose key is <code>$key</code> and whose associated value is <code>$value</code>.</p>
24136+
<p>If <code>$map</code> contains an entry whose key is the <termref
24137+
def="dt-same-key">same key</termref> as <code>$key</code>, the function returns
24138+
a map in which that entry is replaced (at the same relative position)
24139+
with a new entry whose value is <code>$value</code>. It is
24140+
<termref def="implementation-dependent"/> whether the key in the new entry
24141+
takes its original value or is replaced by the supplied <code>$key</code>.
24142+
All other entries in the map are unchanged, and retain their relative order.</p>
2413924143

24140-
<p>The <xtermref spec="DM40" ref="dt-entry-order">entry order</xtermref>
24141-
of the entries in the returned map is as follows:
24142-
if <code>$map</code> contains an entry whose key is <code>$key</code>,
24143-
then the new value replaces the old value and the position of the entry is not changed;
24144-
otherwise, the new entry is added after all existing entries.</p>
24145-
24146-
24144+
<p>Otherwise, when <code>$map</code> contains no such entry, the function
24145+
returns a map containing all entries from the supplied <code>$map</code>
24146+
(retaining their relative position) followed by a new entry whose key
24147+
is <code>$key</code> and whose associated value is <code>$value</code>.</p>
2414724148

2414824149
</fos:rules>
2414924150

@@ -24164,8 +24165,9 @@ declare function map:find($input as item()*,
2416424165
as some existing key present in <code>$map</code>, but nevertheless
2416524166
differs from the existing key in some way:
2416624167
for example, it might have a different type annotation, or it might be an <code>xs:dateTime</code>
24167-
value in a different timezone. In this situation the key that appears in the result map
24168-
is always the supplied <code>$key</code>, not the existing key.</p>
24168+
value in a different timezone. In this situation it is
24169+
<termref def="implementation-dependent"/> whether the key that appears in the result map
24170+
is the supplied <code>$key</code> or the existing key.</p>
2416924171

2417024172
</fos:notes>
2417124173

@@ -24201,7 +24203,12 @@ declare function map:find($input as item()*,
2420124203
</fos:example>
2420224204
</fos:examples>
2420324205
<fos:changes>
24204-
<fos:change issue="1651" PR="1703" date="2025-01-14"><p>Enhanced to allow for ordered maps.</p></fos:change>
24206+
<fos:change issue="1651" PR="1703" date="2025-01-14">
24207+
<p>Enhanced to allow for ordered maps.</p>
24208+
</fos:change>
24209+
<fos:change issue="1725">
24210+
<p>It is no longer guaranteed that the new key replaces the existing key.</p>
24211+
</fos:change>
2420524212
</fos:changes>
2420624213
</fos:function>
2420724214

@@ -24649,9 +24656,9 @@ else map:put($map, $key, $action(()))
2464924656
<item><p>The position of the combined entry in the
2465024657
<xtermref spec="DM40" ref="dt-entry-order"/> of the result map
2465124658
will correspond to the position of the first of the duplicates.</p></item>
24652-
<item><p>The key of the combined entry in the
24653-
<xtermref spec="DM40" ref="dt-entry-order"/> of the result map
24654-
will correspond to the key of the <emph>last</emph> of the duplicates.
24659+
<item><p>The key of the combined entry
24660+
will correspond to the key of one of the duplicates: it is
24661+
<termref def="implementation-dependent"/> which one is chosen.
2465524662
(It is possible for two keys to be considered duplicates even if they differ:
2465624663
for example, they may have different type annotations, or they may
2465724664
be <code>xs:dateTime</code> values in different timezones.) </p></item>

specifications/xpath-functions-40/src/xpath-functions.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13796,6 +13796,12 @@ ISBN 0 521 77752 6.</bibl>
1379613796
longer possible to supply an instance of <code>xs:anyURI</code> or (when XPath 1.0 compatibility
1379713797
mode is in force) an instance of <code>xs:boolean</code> or <code>xs:duration</code>.</p>
1379813798
</item>
13799+
<item diff="add" at="issue1725">
13800+
<p>When <function>fn:put</function> replaces an entry in a map with a new value for an
13801+
existing key, in the case where the existing key and the new key differ (for example,
13802+
if they have different type annotations), it is no longer guaranteed that the new
13803+
entry includes the new key rather than the existing key.</p>
13804+
</item>
1379913805
</olist>
1380013806

1380113807
<p>For compatibility issues regarding earlier versions, see the 3.1 version of this specification.</p>

specifications/xslt-40/src/element-catalog.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1702,7 +1702,7 @@
17021702
<e:attribute name="select">
17031703
<e:data-type name="expression"/>
17041704
</e:attribute>
1705-
<e:attribute name="on-duplicates" required="no" default="fn($a, $b) { error(xs:QName(err:XTDE3365)) }">
1705+
<e:attribute name="duplicates" required="no" default="fn($a, $b) { error(xs:QName(err:XTDE3365)) }">
17061706
<e:data-type name="expression"/>
17071707
</e:attribute>
17081708
<!--<e:attribute name="ordered" default="no">

specifications/xslt-40/src/schema-for-xslt40.rnc

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,10 +1125,9 @@ map.element =
11251125
element map {
11261126
extension.atts,
11271127
global.atts,
1128-
sequence-constructor.model
1128+
select-or-sequence-constructor.model
11291129
}
1130-
# TODO: add @on-duplicates
1131-
# TODO: add @ordering
1130+
# TODO: add @duplicates
11321131

11331132
map-entry.element =
11341133
element map-entry {

specifications/xslt-40/src/schema-for-xslt40.xsd

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,11 +1128,9 @@ of problems processing the schema using various tools
11281128
substitutionGroup="xsl:instruction">
11291129
<xs:complexType>
11301130
<xs:complexContent mixed="true">
1131-
<xs:extension base="xsl:sequence-constructor">
1132-
<xs:attribute name="on-duplicates" type="xsl:expression"/>
1133-
<xs:attribute name="ordering" type="xsl:avt"/>
1134-
<xs:attribute name="_on-duplicates" type="xs:string"/>
1135-
<xs:attribute name="_ordering" type="xs:string"/>
1131+
<xs:extension base="xsl:sequence-constructor-and-select">
1132+
<xs:attribute name="duplicates" type="xsl:expression"/>
1133+
<xs:attribute name="_duplicates" type="xs:string"/>
11361134
</xs:extension>
11371135
</xs:complexContent>
11381136
</xs:complexType>

specifications/xslt-40/src/xslt.xml

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -36126,7 +36126,7 @@ the same group, and the-->
3612636126

3612736127
<changes>
3612836128
<change issue="169" date="2023-11-28">
36129-
A new attribute <code>xsl:map/@on-duplicates</code> is available,
36129+
A new attribute <code>xsl:map/@duplicates</code> is available,
3613036130
allowing control over how duplicate keys are handled by the <elcode>xsl:map</elcode>
3613136131
instruction.
3613236132
</change>
@@ -36138,25 +36138,26 @@ the same group, and the-->
3613836138
and <code>fn:atomic-equal(<var>K</var>, <var>L</var>)</code> returns <code>true</code>.</p>
3613936139

3614036140
<p><error spec="XT" class="DE" code="3365" type="dynamic">
36141-
<p>In the absence of the <code>on-duplicates</code> attribute,
36141+
<p>In the absence of the <code>duplicates</code> attribute,
3614236142
a <termref def="dt-dynamic-error">dynamic error</termref> occurs if the set of
3614336143
keys in the maps making up the input sequence
3614436144
<error.extra>of an <elcode>xsl:map</elcode> instruction</error.extra>
3614536145
contains duplicates.</p>
3614636146
</error></p>
3614736147

36148-
<p>The result of evaluating the <code>on-duplicates</code> attribute, if present, <rfc2119>must</rfc2119>
36149-
be a function with arity 2. When the <elcode>xsl:map</elcode> instruction encounters two
36150-
map entries having the same key, the two values associated with this key are passed as
36151-
arguments to this function, and the function returns the value that should be associated
36152-
with this key in the final map.</p>
36153-
36154-
<p>More formally, the result of the <elcode>xsl:map</elcode> instruction is defined by reference to
36155-
the function <xfunction>map:merge</xfunction>. Specifically, if <code>$maps</code>
36156-
is the input sequence to <elcode>xsl:map</elcode>, and <code>$combine</code>
36157-
is the <termref def="dt-effective-value"/> of the <code>on-duplicates</code>
36148+
<p>The result of evaluating the <code>duplicates</code> attribute, if present, <rfc2119>must</rfc2119>
36149+
be either one of the strings <code>"use-first"</code>, <code>"use-last"</code>,
36150+
<code>"use-any"</code>, <code>"combine"</code>, or <code>"reject"</code>,
36151+
or a function with arity 2. These values correspond to the permitted
36152+
values of the <code>duplicates</code> option of the
36153+
<xfunction>map:of-pairs</xfunction> function.</p>
36154+
36155+
<p>The result of the <elcode>xsl:map</elcode> instruction is defined by reference to
36156+
the function <xfunction>map:of-pairs</xfunction>. Specifically, if <code>$maps</code>
36157+
is the input sequence to <elcode>xsl:map</elcode>, and <code>$duplicates</code>
36158+
is the <termref def="dt-effective-value"/> of the <code>duplicates</code>
3615836159
attribute, then the result of the instruction is the result of the function
36159-
call <code>map:merge($maps, { "combine": $combine })</code>.</p>
36160+
call <code>map:of-pairs(map:pairs($maps), { "duplicates": $duplicates })</code>.</p>
3616036161

3616136162
<!--
3616236163

@@ -36182,8 +36183,8 @@ the same group, and the-->
3618236183
<p>Thus, if the values are all singleton items (which is not necessarily the case), and if the sequence
3618336184
of values is <var>S</var>, then the final result is <code>fold-left(tail(S), head(S), F)</code>.</p>
3618436185
-->
36185-
<p>For example, the following table shows some useful callback functions that might be supplied
36186-
as the value of the <code>on-duplicates</code> attribute, and explains their effect:</p>
36186+
<p>The following table shows some possible values
36187+
of the <code>duplicates</code> attribute, and explains their effect:</p>
3618736188

3618836189
<table>
3618936190
<thead>
@@ -36194,41 +36195,47 @@ the same group, and the-->
3619436195
</thead>
3619536196
<tbody>
3619636197
<tr>
36197-
<td><code>fn($a, $b) { $a }</code></td>
36198+
<td><code>duplicates="use-first"</code></td>
3619836199
<td>The first of the duplicate values is used.</td>
3619936200
</tr>
3620036201
<tr>
36201-
<td><code>fn($a, $b) { $b }</code></td>
36202+
<td><code>duplicates="use-last"</code></td>
3620236203
<td>The last of the duplicate values is used.</td>
3620336204
</tr>
3620436205
<tr>
36205-
<td><code>fn($a, $b) { $a, $b }</code></td>
36206+
<td><code>duplicates="combine"</code></td>
3620636207
<td>The <xtermref spec="XP40" ref="dt-sequence-concatenation"/>
3620736208
of the duplicate values is used. This could
3620836209
also be expressed as <code>on-duplicates="op(',')"</code>.</td>
3620936210
</tr>
3621036211
<tr>
36211-
<td><code>fn($a, $b) { max(($a, $b)) }</code></td>
36212+
<td><code>duplicates="fn($a, $b) { max(($a, $b)) }"</code></td>
3621236213
<td>The highest of the duplicate values is used.</td>
3621336214
</tr>
3621436215
<tr>
36215-
<td><code>fn($a, $b) { min(($a, $b)) }</code></td>
36216+
<td><code>duplicates="fn($a, $b) { min(($a, $b)) }"</code></td>
3621636217
<td>The lowest of the duplicate values is used.</td>
3621736218
</tr>
3621836219
<tr>
36219-
<td><code>concat(?, ', ', ?) }</code></td>
36220+
<td><code>duplicates="concat(?, ', ', ?) }"</code></td>
3622036221
<td>The comma-separated string concatenation of the duplicate values is used.</td>
3622136222
</tr>
36222-
<tr diff="add" at="2023-04-04">
36223-
<td><code>fn($a, $b) { $a + $b }</code></td>
36224-
<td>The sum of the duplicate values is used.
36225-
This could also be expressed as <code>on-duplicates="op('+')"</code>
36223+
<tr>
36224+
<td><code>duplicates="op('+')"</code></td>
36225+
<td>The sum of the duplicate values is used.</td>
36226+
</tr>
36227+
<tr>
36228+
<td><code>duplicates="fn($a, $b) { subsequence(($a, $b), 1, 4) }"</code></td>
36229+
<td>The first four of the duplicates are retained; any further duplicates
36230+
are discarded.
3622636231
</td>
3622736232
</tr>
3622836233
<tr>
36229-
<td><code>fn($a, $b) { error() }</code></td>
36230-
<td>Duplicates are rejected as an error (this is the default in the absence of the
36231-
<code>on-duplicates</code> attribute).</td>
36234+
<td><code>duplicates="fn($a, $b) { distinct-values(($a, $b)) }"</code></td>
36235+
<td>When multiple entries have the same key, the corresponding values
36236+
are retained only if they are distinct from other values having the
36237+
same key.
36238+
</td>
3623236239
</tr>
3623336240
</tbody>
3623436241
</table>
@@ -36246,7 +36253,7 @@ the same group, and the-->
3624636253
<eg><![CDATA[{ "A23": [ 12, 2 ], "A24": [ 5 ], "A23": [ 9 ] }]]></eg>
3624736254
<p>The logic is:</p>
3624836255
<eg><![CDATA[<xsl:template match="data">
36249-
<xsl:map on-duplicates="fn($a, $b) { array:join(($a, $b)) }">
36256+
<xsl:map duplicates="fn($a, $b) { array:join(($a, $b)) }">
3625036257
<xsl:for-each select="event">
3625136258
<xsl:map-entry key="@id" select="[xs:integer(@value)]"/>
3625236259
</xsl:for-each>
@@ -36255,14 +36262,16 @@ the same group, and the-->
3625536262
</example>
3625636263

3625736264
<note>
36258-
<p>Specifying the effect by reference to <xfunction>map:merge</xfunction> has
36265+
<p>Specifying the effect by reference to <xfunction>map:of-pairs</xfunction> has
3625936266
the following consequences when duplicates are combined
3626036267
into a merged entry:</p>
3626136268
<ulist>
3626236269
<item><p>The position of the merged entry in the result corresponds
3626336270
to the position of the first of the duplicate keys in the input.</p></item>
3626436271
<item><p>The key used for the merged entry in the result corresponds
36265-
to the last of the duplicate keys in the input. This is relevant when
36272+
to one of the duplicate keys in the input: it is
36273+
<termref def="dt-implementation-dependent"/> which one is chosen.
36274+
This is relevant when
3626636275
the duplicate keys differ in some way, for example when they have
3626736276
different type annotations, or when they are <code>xs:dateTime</code>
3626836277
values in different timezones.</p></item>

0 commit comments

Comments
 (0)