Skip to content

Commit d82b3b8

Browse files
committed
add assignMany, concat, and concatMany for Dict
1 parent c8d05be commit d82b3b8

4 files changed

Lines changed: 286 additions & 8 deletions

File tree

packages/@rescript/runtime/Stdlib_Dict.res

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ external fromIterable: Stdlib_Iterable.t<(string, 'a)> => dict<'a> = "Object.fro
2323

2424
@val external assign: (dict<'a>, dict<'a>) => dict<'a> = "Object.assign"
2525

26+
@variadic @val external assignMany: (dict<'a>, array<dict<'a>>) => dict<'a> = "Object.assign"
27+
28+
@val external concat: (@as(json`{}`) _, dict<'a>, dict<'a>) => dict<'a> = "Object.assign"
29+
30+
@variadic @val
31+
external concatMany: (@as(json`{}`) _, dict<'a>, array<dict<'a>>) => dict<'a> = "Object.assign"
32+
2633
@val external copy: (@as(json`{}`) _, dict<'a>) => dict<'a> = "Object.assign"
2734

2835
// Use %raw to support for..in which is a ~10% faster than .forEach

packages/@rescript/runtime/Stdlib_Dict.resi

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ external valuesToArray: dict<'a> => array<'a> = "Object.values"
222222
/**
223223
`assign(dictionary1, dictionary2)` [shallowly](https://developer.mozilla.org/en-US/docs/Glossary/Shallow_copy) merges dictionary2 into dictionary1, and returns dictionary1.
224224
225-
Beware this will *mutate* dictionary1. If you're looking for a way to copy a dictionary, check out `Dict.copy`.
225+
Beware this will *mutate* dictionary1. If you're looking for a fresh merged dictionary instead, check out `Dict.concat`.
226226
227227
## Examples
228228
```rescript
@@ -242,6 +242,67 @@ Console.log(dict1->Dict.keysToArray) // Logs `["firstKey", "someKey", "someKey2"
242242
@val
243243
external assign: (dict<'a>, dict<'a>) => dict<'a> = "Object.assign"
244244

245+
/**
246+
`assignMany(target, sources)` [shallowly](https://developer.mozilla.org/en-US/docs/Glossary/Shallow_copy) merges each dictionary in `sources` into `target`, and returns `target`.
247+
248+
Beware this will *mutate* `target`. Later dictionaries overwrite earlier ones. If you're looking for a fresh merged dictionary instead, check out `Dict.concatMany`.
249+
250+
## Examples
251+
```rescript
252+
let target = dict{"firstKey": 1}
253+
let result = target->Dict.assignMany([
254+
dict{"someKey": 2},
255+
dict{"someKey": 3, "someKey2": 4},
256+
])
257+
258+
assertEqual(result, dict{"firstKey": 1, "someKey": 3, "someKey2": 4})
259+
assertEqual(result === target, true)
260+
```
261+
*/
262+
@variadic @val
263+
external assignMany: (dict<'a>, array<dict<'a>>) => dict<'a> = "Object.assign"
264+
265+
/**
266+
`concat(dictionary1, dictionary2)` [shallowly](https://developer.mozilla.org/en-US/docs/Glossary/Shallow_copy) merges `dictionary1` and `dictionary2` into a fresh dictionary, and returns the new dictionary.
267+
268+
Neither input dictionary is mutated. If both dictionaries contain the same key, the value from `dictionary2` overwrites the one from `dictionary1`.
269+
270+
## Examples
271+
```rescript
272+
let dict1 = dict{"firstKey": 1}
273+
let dict2 = dict{"firstKey": 2, "someKey": 3}
274+
275+
let merged = dict1->Dict.concat(dict2)
276+
277+
assertEqual(dict1, dict{"firstKey": 1})
278+
assertEqual(merged, dict{"firstKey": 2, "someKey": 3})
279+
assertEqual(merged === dict1, false)
280+
```
281+
*/
282+
@val
283+
external concat: (@as(json`{}`) _, dict<'a>, dict<'a>) => dict<'a> = "Object.assign"
284+
285+
/**
286+
`concatMany(target, sources)` [shallowly](https://developer.mozilla.org/en-US/docs/Glossary/Shallow_copy) merges `target` and each dictionary in `sources` into a fresh dictionary, and returns the new dictionary.
287+
288+
Neither `target` nor any dictionary in `sources` is mutated. Later dictionaries overwrite earlier ones when they contain the same key.
289+
290+
## Examples
291+
```rescript
292+
let target = dict{"firstKey": 1}
293+
let merged = target->Dict.concatMany([
294+
dict{"someKey": 2},
295+
dict{"someKey": 3, "someKey2": 4},
296+
])
297+
298+
assertEqual(target, dict{"firstKey": 1})
299+
assertEqual(merged, dict{"firstKey": 1, "someKey": 3, "someKey2": 4})
300+
assertEqual(merged === target, false)
301+
```
302+
*/
303+
@variadic @val
304+
external concatMany: (@as(json`{}`) _, dict<'a>, array<dict<'a>>) => dict<'a> = "Object.assign"
305+
245306
/**
246307
`copy(dictionary)` [shallowly copies](https://developer.mozilla.org/en-US/docs/Glossary/Shallow_copy) the provided dictionary to a new dictionary.
247308

tests/tests/src/stdlib/Stdlib_DictTests.mjs

Lines changed: 139 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,142 @@ Test.run([
8787
]
8888
]), eq, {foo: "bar", baz: "qux"});
8989

90+
let target = {
91+
a: 1,
92+
b: 2
93+
};
94+
95+
let result = Object.assign(target, {
96+
b: 3
97+
}, {
98+
b: 4,
99+
c: 0
100+
});
101+
102+
Test.run([
103+
[
104+
"Stdlib_DictTests.res",
105+
58,
106+
15,
107+
57
108+
],
109+
"assignMany copies from sources to target"
110+
], result, eq, {
111+
a: 1,
112+
b: 4,
113+
c: 0
114+
});
115+
116+
Test.run([
117+
[
118+
"Stdlib_DictTests.res",
119+
67,
120+
22,
121+
49
122+
],
123+
"assignMany mutates target"
124+
], result === target, eq, true);
125+
126+
let target$1 = {
127+
a: 1,
128+
b: 2
129+
};
130+
131+
let result$1 = Object.assign({}, target$1, {
132+
b: 3,
133+
c: 0
134+
});
135+
136+
Test.run([
137+
[
138+
"Stdlib_DictTests.res",
139+
78,
140+
15,
141+
54
142+
],
143+
"concat copies into a fresh dictionary"
144+
], result$1, eq, {
145+
a: 1,
146+
b: 3,
147+
c: 0
148+
});
149+
150+
Test.run([
151+
[
152+
"Stdlib_DictTests.res",
153+
88,
154+
15,
155+
47
156+
],
157+
"concat leaves target unchanged"
158+
], target$1, eq, {
159+
a: 1,
160+
b: 2
161+
});
162+
163+
Test.run([
164+
[
165+
"Stdlib_DictTests.res",
166+
96,
167+
22,
168+
57
169+
],
170+
"concat returns a fresh dictionary"
171+
], result$1 === target$1, eq, false);
172+
173+
let target$2 = {
174+
a: 1,
175+
b: 2
176+
};
177+
178+
let result$2 = Object.assign({}, target$2, {
179+
b: 3
180+
}, {
181+
b: 4,
182+
c: 0
183+
});
184+
185+
Test.run([
186+
[
187+
"Stdlib_DictTests.res",
188+
107,
189+
15,
190+
58
191+
],
192+
"concatMany copies into a fresh dictionary"
193+
], result$2, eq, {
194+
a: 1,
195+
b: 4,
196+
c: 0
197+
});
198+
199+
Test.run([
200+
[
201+
"Stdlib_DictTests.res",
202+
117,
203+
15,
204+
51
205+
],
206+
"concatMany leaves target unchanged"
207+
], target$2, eq, {
208+
a: 1,
209+
b: 2
210+
});
211+
212+
Test.run([
213+
[
214+
"Stdlib_DictTests.res",
215+
125,
216+
22,
217+
61
218+
],
219+
"concatMany returns a fresh dictionary"
220+
], result$2 === target$2, eq, false);
221+
90222
Test.run([
91223
[
92224
"Stdlib_DictTests.res",
93-
51,
225+
129,
94226
13,
95227
35
96228
],
@@ -103,7 +235,7 @@ Test.run([
103235
Test.run([
104236
[
105237
"Stdlib_DictTests.res",
106-
57,
238+
135,
107239
13,
108240
34
109241
],
@@ -118,7 +250,7 @@ let dict = {
118250
Test.run([
119251
[
120252
"Stdlib_DictTests.res",
121-
69,
253+
147,
122254
22,
123255
38
124256
],
@@ -128,7 +260,7 @@ Test.run([
128260
Test.run([
129261
[
130262
"Stdlib_DictTests.res",
131-
70,
263+
148,
132264
22,
133265
43
134266
],
@@ -138,7 +270,7 @@ Test.run([
138270
Test.run([
139271
[
140272
"Stdlib_DictTests.res",
141-
71,
273+
149,
142274
22,
143275
37
144276
],
@@ -148,7 +280,7 @@ Test.run([
148280
Test.run([
149281
[
150282
"Stdlib_DictTests.res",
151-
72,
283+
150,
152284
22,
153285
39
154286
],
@@ -158,7 +290,7 @@ Test.run([
158290
Test.run([
159291
[
160292
"Stdlib_DictTests.res",
161-
74,
293+
152,
162294
15,
163295
51
164296
],

tests/tests/src/stdlib/Stdlib_DictTests.res

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,84 @@ Test.run(
4747
%raw(`{foo: "bar", baz: "qux"}`),
4848
)
4949

50+
{
51+
let target = dict{
52+
"a": 1,
53+
"b": 2,
54+
}
55+
let result = target->Dict.assignMany([dict{"b": 3}, dict{"b": 4, "c": 0}])
56+
57+
Test.run(
58+
__POS_OF__("assignMany copies from sources to target"),
59+
result,
60+
eq,
61+
dict{
62+
"a": 1,
63+
"b": 4,
64+
"c": 0,
65+
},
66+
)
67+
Test.run(__POS_OF__("assignMany mutates target"), result === target, eq, true)
68+
}
69+
70+
{
71+
let target = dict{
72+
"a": 1,
73+
"b": 2,
74+
}
75+
let result = target->Dict.concat(dict{"b": 3, "c": 0})
76+
77+
Test.run(
78+
__POS_OF__("concat copies into a fresh dictionary"),
79+
result,
80+
eq,
81+
dict{
82+
"a": 1,
83+
"b": 3,
84+
"c": 0,
85+
},
86+
)
87+
Test.run(
88+
__POS_OF__("concat leaves target unchanged"),
89+
target,
90+
eq,
91+
dict{
92+
"a": 1,
93+
"b": 2,
94+
},
95+
)
96+
Test.run(__POS_OF__("concat returns a fresh dictionary"), result === target, eq, false)
97+
}
98+
99+
{
100+
let target = dict{
101+
"a": 1,
102+
"b": 2,
103+
}
104+
let result = target->Dict.concatMany([dict{"b": 3}, dict{"b": 4, "c": 0}])
105+
106+
Test.run(
107+
__POS_OF__("concatMany copies into a fresh dictionary"),
108+
result,
109+
eq,
110+
dict{
111+
"a": 1,
112+
"b": 4,
113+
"c": 0,
114+
},
115+
)
116+
Test.run(
117+
__POS_OF__("concatMany leaves target unchanged"),
118+
target,
119+
eq,
120+
dict{
121+
"a": 1,
122+
"b": 2,
123+
},
124+
)
125+
Test.run(__POS_OF__("concatMany returns a fresh dictionary"), result === target, eq, false)
126+
}
127+
50128
Test.run(
51129
__POS_OF__("getUnsafe - existing"),
52130
Dict.fromArray([("foo", "bar")])->Dict.getUnsafe("foo"),

0 commit comments

Comments
 (0)