@grain/stdlib 0.6.5 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/LICENSE +1 -1
  3. package/README.md +2 -2
  4. package/array.gr +55 -7
  5. package/array.md +123 -77
  6. package/bigint.md +30 -30
  7. package/buffer.gr +20 -53
  8. package/buffer.md +47 -47
  9. package/bytes.gr +111 -35
  10. package/bytes.md +111 -32
  11. package/char.gr +201 -99
  12. package/char.md +361 -34
  13. package/exception.gr +11 -11
  14. package/exception.md +26 -1
  15. package/float32.gr +327 -3
  16. package/float32.md +606 -19
  17. package/float64.gr +320 -3
  18. package/float64.md +606 -19
  19. package/fs.gr +1082 -0
  20. package/fs.md +630 -0
  21. package/hash.gr +142 -88
  22. package/hash.md +102 -14
  23. package/int16.md +23 -23
  24. package/int32.gr +25 -4
  25. package/int32.md +65 -30
  26. package/int64.gr +26 -1
  27. package/int64.md +65 -30
  28. package/int8.md +23 -23
  29. package/json.gr +366 -51
  30. package/json.md +418 -2
  31. package/list.gr +387 -49
  32. package/list.md +492 -69
  33. package/map.gr +20 -12
  34. package/map.md +44 -38
  35. package/marshal.gr +41 -40
  36. package/marshal.md +2 -2
  37. package/number.gr +159 -30
  38. package/number.md +215 -38
  39. package/option.md +21 -21
  40. package/package.json +5 -3
  41. package/path.gr +48 -0
  42. package/path.md +103 -12
  43. package/pervasives.gr +2 -2
  44. package/pervasives.md +37 -37
  45. package/priorityqueue.gr +7 -7
  46. package/priorityqueue.md +19 -19
  47. package/queue.gr +183 -29
  48. package/queue.md +296 -40
  49. package/random.md +6 -6
  50. package/range.gr +4 -4
  51. package/range.md +6 -6
  52. package/rational.md +16 -16
  53. package/regex.gr +52 -51
  54. package/regex.md +11 -11
  55. package/result.md +16 -16
  56. package/runtime/atof/common.md +39 -39
  57. package/runtime/atof/decimal.gr +6 -6
  58. package/runtime/atof/decimal.md +8 -8
  59. package/runtime/atof/lemire.gr +5 -5
  60. package/runtime/atof/lemire.md +1 -1
  61. package/runtime/atof/parse.gr +16 -16
  62. package/runtime/atof/parse.md +2 -2
  63. package/runtime/atof/slow.md +1 -1
  64. package/runtime/atof/table.md +2 -2
  65. package/runtime/atoi/parse.gr +3 -3
  66. package/runtime/atoi/parse.md +1 -1
  67. package/runtime/bigint.gr +15 -47
  68. package/runtime/bigint.md +54 -60
  69. package/runtime/compare.gr +2 -2
  70. package/runtime/compare.md +1 -1
  71. package/runtime/dataStructures.md +33 -33
  72. package/runtime/debugPrint.gr +4 -1
  73. package/runtime/debugPrint.md +9 -9
  74. package/runtime/equal.gr +99 -77
  75. package/runtime/equal.md +1 -1
  76. package/runtime/exception.gr +62 -82
  77. package/runtime/exception.md +62 -11
  78. package/runtime/gc.gr +39 -45
  79. package/runtime/gc.md +4 -4
  80. package/runtime/malloc.gr +7 -7
  81. package/runtime/malloc.md +4 -4
  82. package/runtime/math/kernel/cos.gr +70 -0
  83. package/runtime/math/kernel/cos.md +14 -0
  84. package/runtime/math/kernel/sin.gr +65 -0
  85. package/runtime/math/kernel/sin.md +14 -0
  86. package/runtime/math/kernel/tan.gr +136 -0
  87. package/runtime/math/kernel/tan.md +14 -0
  88. package/runtime/math/rempio2.gr +244 -0
  89. package/runtime/math/rempio2.md +14 -0
  90. package/runtime/math/trig.gr +130 -0
  91. package/runtime/math/trig.md +28 -0
  92. package/runtime/math/umuldi.gr +26 -0
  93. package/runtime/math/umuldi.md +14 -0
  94. package/runtime/numberUtils.gr +29 -29
  95. package/runtime/numberUtils.md +12 -12
  96. package/runtime/numbers.gr +373 -381
  97. package/runtime/numbers.md +79 -73
  98. package/runtime/string.gr +37 -105
  99. package/runtime/string.md +3 -9
  100. package/runtime/unsafe/constants.md +24 -24
  101. package/runtime/unsafe/conv.md +13 -13
  102. package/runtime/unsafe/memory.gr +24 -20
  103. package/runtime/unsafe/memory.md +27 -7
  104. package/runtime/unsafe/offsets.gr +36 -0
  105. package/runtime/unsafe/offsets.md +88 -0
  106. package/runtime/unsafe/panic.gr +28 -0
  107. package/runtime/unsafe/panic.md +14 -0
  108. package/runtime/unsafe/tags.md +32 -32
  109. package/runtime/unsafe/wasmf32.md +28 -28
  110. package/runtime/unsafe/wasmf64.md +28 -28
  111. package/runtime/unsafe/wasmi32.md +47 -47
  112. package/runtime/unsafe/wasmi64.md +50 -50
  113. package/runtime/utf8.gr +189 -0
  114. package/runtime/utf8.md +117 -0
  115. package/runtime/wasi.gr +4 -2
  116. package/runtime/wasi.md +138 -138
  117. package/set.gr +18 -11
  118. package/set.md +42 -36
  119. package/stack.gr +171 -2
  120. package/stack.md +297 -15
  121. package/string.gr +352 -557
  122. package/string.md +77 -34
  123. package/uint16.gr +81 -0
  124. package/uint16.md +183 -22
  125. package/uint32.gr +25 -4
  126. package/uint32.md +63 -28
  127. package/uint64.gr +25 -5
  128. package/uint64.md +63 -28
  129. package/uint8.gr +81 -0
  130. package/uint8.md +183 -22
  131. package/uri.gr +57 -53
  132. package/uri.md +11 -12
  133. package/wasi/file.gr +67 -59
  134. package/wasi/file.md +39 -39
  135. package/wasi/process.md +5 -5
  136. package/wasi/random.md +3 -3
  137. package/wasi/time.md +4 -4
  138. package/runtime/utils/printing.gr +0 -60
  139. package/runtime/utils/printing.md +0 -26
package/list.gr CHANGED
@@ -9,6 +9,28 @@
9
9
  */
10
10
  module List
11
11
 
12
+ from "runtime/unsafe/memory" include Memory
13
+ from "runtime/unsafe/wasmi32" include WasmI32
14
+ from "runtime/dataStructures" include DataStructures
15
+
16
+ /**
17
+ * Creates a new list with all elements in reverse order.
18
+ *
19
+ * @param list: The list to reverse
20
+ * @returns The new list
21
+ *
22
+ * @since v0.1.0
23
+ */
24
+ provide let reverse = list => {
25
+ let rec revHelp = (list, acc) => {
26
+ match (list) {
27
+ [] => acc,
28
+ [first, ...rest] => revHelp(rest, [first, ...acc]),
29
+ }
30
+ }
31
+ revHelp(list, [])
32
+ }
33
+
12
34
  /**
13
35
  * Creates a new list of the specified length where each element is
14
36
  * initialized with the result of an initializer function. The initializer
@@ -23,17 +45,18 @@ module List
23
45
  * @since v0.3.0
24
46
  */
25
47
  provide let init = (length, fn) => {
26
- // This method can be further improved by checking the length against a specific size
27
- // and determining if it can be made tail-recursive, then use List.reverse on it.
28
- // Which would be the same as OCaml does it in https://github.com/ocaml/ocaml/blob/03839754f46319aa36d9dad56940a6f3c3bcb48a/stdlib/list.ml#L79
29
- let rec iter = (idx, length) => {
30
- if (idx >= length) {
31
- []
32
- } else {
33
- [fn(idx), ...iter(idx + 1, length)]
48
+ if (length < 2048) {
49
+ // For small lists, use a faster non-tail-recursive version
50
+ let rec iter = (idx, length) => {
51
+ if (idx >= length) [] else [fn(idx), ...iter(idx + 1, length)]
34
52
  }
53
+ iter(0, length)
54
+ } else {
55
+ let rec iter = (idx, length, acc) => {
56
+ if (idx >= length) acc else iter(idx + 1, length, [fn(idx), ...acc])
57
+ }
58
+ reverse(iter(0, length, []))
35
59
  }
36
- iter(0, length)
37
60
  }
38
61
 
39
62
  /**
@@ -70,24 +93,6 @@ provide let isEmpty = list => {
70
93
  }
71
94
  }
72
95
 
73
- /**
74
- * Creates a new list with all elements in reverse order.
75
- *
76
- * @param list: The list to reverse
77
- * @returns The new list
78
- *
79
- * @since v0.1.0
80
- */
81
- provide let reverse = list => {
82
- let rec iter = (list, acc) => {
83
- match (list) {
84
- [] => acc,
85
- [first, ...rest] => iter(rest, [first, ...acc]),
86
- }
87
- }
88
- iter(list, [])
89
- }
90
-
91
96
  /**
92
97
  * Creates a new list with the elements of the first list followed by
93
98
  * the elements of the second list.
@@ -213,6 +218,56 @@ provide let mapi = (fn, list) => {
213
218
  iter(fn, list, 0)
214
219
  }
215
220
 
221
+ /**
222
+ * Produces a new list initialized with the results of a mapper function
223
+ * called on each element of the input list.
224
+ * The mapper function can return `None` to exclude the element from the new list.
225
+ *
226
+ * @param fn: The mapper function to call on each element, where the value returned will be used to initialize the element in the new list
227
+ * @param list: The list to iterate
228
+ * @returns The new list with filtered mapped values
229
+ *
230
+ * @example List.filterMap(x => if (x % 2 == 0) Some(toString(x)) else None, [1, 2, 3, 4]) == ["2", "4"]
231
+ *
232
+ * @since v0.7.0
233
+ */
234
+ provide let rec filterMap = (fn, list) => {
235
+ match (list) {
236
+ [] => [],
237
+ [first, ...rest] => match (fn(first)) {
238
+ Some(v) => [v, ...filterMap(fn, rest)],
239
+ None => filterMap(fn, rest),
240
+ },
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Produces a new list initialized with the results of a mapper function
246
+ * called on each element of the input list and its index.
247
+ * The mapper function can return `None` to exclude the element from the new list.
248
+ *
249
+ * @param fn: The mapper function to call on each element, where the value returned will be used to initialize the element in the new list
250
+ * @param list: The list to iterate
251
+ * @returns The new list with filtered mapped values
252
+ *
253
+ * @example List.filterMapi((x, i) => if (x % 2 == 0) Some(toString(x)) else None, [1, 2, 3, 4]) == ["2", "4"]
254
+ * @example List.filterMapi((x, i) => if (i == 0) Some(toString(x)) else None, [1, 2, 3, 4]) == ["1"]
255
+ *
256
+ * @since v0.7.0
257
+ */
258
+ provide let filterMapi = (fn, list) => {
259
+ let rec iter = (fn, list, index) => {
260
+ match (list) {
261
+ [] => [],
262
+ [first, ...rest] => match (fn(first, index)) {
263
+ Some(v) => [v, ...iter(fn, rest, index + 1)],
264
+ None => iter(fn, rest, index + 1),
265
+ },
266
+ }
267
+ }
268
+ iter(fn, list, 0)
269
+ }
270
+
216
271
  /**
217
272
  * Produces a new list by calling a function on each element
218
273
  * of the input list. Each iteration produces an intermediate
@@ -238,7 +293,7 @@ provide let rec flatMap = (fn, list) => {
238
293
  *
239
294
  * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
240
295
  * @param list: The list to check
241
- * @returns `true` if all elements satify the condition or `false` otherwise
296
+ * @returns `true` if all elements satisfy the condition or `false` otherwise
242
297
  *
243
298
  * @since v0.1.0
244
299
  */
@@ -256,7 +311,7 @@ provide let rec every = (fn, list) => {
256
311
  *
257
312
  * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
258
313
  * @param list: The list to iterate
259
- * @returns `true` if one or more elements satify the condition or `false` otherwise
314
+ * @returns `true` if one or more elements satisfy the condition or `false` otherwise
260
315
  *
261
316
  * @since v0.1.0
262
317
  */
@@ -765,7 +820,7 @@ provide let rec takeWhile = (fn, list) => {
765
820
  }
766
821
 
767
822
  /**
768
- * Finds the first element in a list that satifies the given condition.
823
+ * Finds the first element in a list that satisfies the given condition.
769
824
  *
770
825
  * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
771
826
  * @param list: The list to search
@@ -783,7 +838,7 @@ provide let rec find = (fn, list) => {
783
838
  }
784
839
 
785
840
  /**
786
- * Finds the first index in a list where the element satifies the given condition.
841
+ * Finds the first index in a list where the element satisfies the given condition.
787
842
  *
788
843
  * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
789
844
  * @param list: The list to search
@@ -804,6 +859,31 @@ provide let findIndex = (fn, list) => {
804
859
  findItemIndex(list, 0)
805
860
  }
806
861
 
862
+ /**
863
+ * Finds the first element in a list that satisfies the given condition and
864
+ * returns the result of applying a mapper function to it.
865
+ *
866
+ * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
867
+ * @param list: The list to search
868
+ * @returns `Some(mapped)` containing the first value found with the given mapping or `None` otherwise
869
+ *
870
+ * @example
871
+ * let jsonObject = [(1, 'a'), (2, 'b'), (1, 'c')]
872
+ * let getItem = (key, obj) => List.findMap(((k, v)) => if (k == key) Some(v) else None, obj)
873
+ * assert getItem(1, jsonObject) == Some('a')
874
+ *
875
+ * @since v0.7.0
876
+ */
877
+ provide let rec findMap = (fn, list) => {
878
+ match (list) {
879
+ [] => None,
880
+ [first, ...rest] => match (fn(first)) {
881
+ None => findMap(fn, rest),
882
+ Some(v) => Some(v),
883
+ },
884
+ }
885
+ }
886
+
807
887
  /**
808
888
  * Combines two lists into a Cartesian product of tuples containing
809
889
  * all ordered pairs `(a, b)`.
@@ -842,6 +922,51 @@ provide let sub = (start, length, list) => {
842
922
  take(length, drop(start, list))
843
923
  }
844
924
 
925
+ // List.join helpers
926
+ @unsafe
927
+ let rec computeJoinSize = (sepSize: WasmI32, size: WasmI32, lst: List<String>) => {
928
+ use WasmI32.{ (+) }
929
+ use DataStructures.{ stringSize }
930
+ match (lst) {
931
+ [] => size,
932
+ [hd] => size + stringSize(WasmI32.fromGrain(hd)),
933
+ [hd, ...tl] => {
934
+ let size = size + stringSize(WasmI32.fromGrain(hd)) + sepSize
935
+ ignore(hd)
936
+ computeJoinSize(sepSize, size, tl)
937
+ },
938
+ }
939
+ }
940
+ @unsafe
941
+ let rec buildJoinedString = (
942
+ strPtr: WasmI32,
943
+ sepPtr: WasmI32,
944
+ sepSize: WasmI32,
945
+ offset: WasmI32,
946
+ lst: List<String>,
947
+ ) => {
948
+ use WasmI32.{ (+) }
949
+ use DataStructures.{ stringSize }
950
+ match (lst) {
951
+ [] => void,
952
+ // Last element
953
+ [hd] => {
954
+ let ptr = WasmI32.fromGrain(hd)
955
+ let size = stringSize(ptr)
956
+ Memory.copy(offset, ptr + 8n, size)
957
+ ignore(hd)
958
+ },
959
+ [hd, ...tl] => {
960
+ let ptr = WasmI32.fromGrain(hd)
961
+ let size = stringSize(ptr)
962
+ Memory.copy(offset, ptr + 8n, size)
963
+ ignore(hd)
964
+ let offset = offset + size
965
+ Memory.copy(offset, sepPtr, sepSize)
966
+ buildJoinedString(strPtr, sepPtr, sepSize, offset + sepSize, tl)
967
+ },
968
+ }
969
+ }
845
970
  /**
846
971
  * Combine the given list of strings into one string with the specified
847
972
  * separator inserted between each item.
@@ -852,25 +977,17 @@ provide let sub = (start, length, list) => {
852
977
  *
853
978
  * @since v0.4.0
854
979
  */
980
+ @unsafe
855
981
  provide let join = (separator: String, list: List<String>) => {
856
- let rec iter = (sep, acc, rem) => {
857
- match (rem) {
858
- [] => acc,
859
- [hd, ...tl] => {
860
- let newAcc = match (acc) {
861
- None => Some(hd),
862
- Some(s) => Some(hd ++ sep ++ s),
863
- }
864
- iter(sep, newAcc, tl)
865
- },
866
- }
867
- }
868
-
869
- // Reverse and reduce to take advantage of TCE
870
- match (iter(separator, None, reverse(list))) {
871
- None => "",
872
- Some(s) => s,
873
- }
982
+ use WasmI32.{ (+), (-), (<=) }
983
+ use DataStructures.{ allocateString, stringSize }
984
+ let sepPtr = WasmI32.fromGrain(separator)
985
+ let sepSize = stringSize(sepPtr)
986
+ let strSize = computeJoinSize(sepSize, 0n, list)
987
+ let newString = allocateString(strSize)
988
+ buildJoinedString(newString, sepPtr + 8n, sepSize, newString + 8n, list)
989
+ ignore(sepPtr)
990
+ return WasmI32.toGrain(newString): String
874
991
  }
875
992
 
876
993
  /**
@@ -931,3 +1048,224 @@ provide let sort = (compare=compare, list) => {
931
1048
 
932
1049
  mergesort(list)
933
1050
  }
1051
+
1052
+ /**
1053
+ * Utilities for working with lists of key-key value pairs.
1054
+ *
1055
+ * @example
1056
+ * let data = [
1057
+ * ("name", "Alice"),
1058
+ * ("age", "30"),
1059
+ * ]
1060
+ * assert List.Associative.get("name", data) == Some("Alice")
1061
+ *
1062
+ * @since v0.7.0
1063
+ */
1064
+ provide module Associative {
1065
+ /**
1066
+ * Checks if the given key is present in the list of key-value pairs.
1067
+ *
1068
+ * @param key: The key to search for
1069
+ * @param list: The list of key-value pairs
1070
+ *
1071
+ * @returns `true` if the key is found or `false` otherwise
1072
+ *
1073
+ * @example
1074
+ * let data = [
1075
+ * ("name", "Alice"),
1076
+ * ("age", "30"),
1077
+ * ]
1078
+ * assert List.Associative.has("name", data) == true
1079
+ * @example List.Associative.has("age", []) == false
1080
+ *
1081
+ * @since v0.7.0
1082
+ */
1083
+ provide let rec has = (key, list) => {
1084
+ match (list) {
1085
+ [] => false,
1086
+ [(k, _), ...rest] when key == k => true,
1087
+ [_, ...rest] => has(key, rest),
1088
+ }
1089
+ }
1090
+
1091
+ /**
1092
+ * Retrieves the first value in the list of key-value pairs that matches the given key.
1093
+ *
1094
+ * @param key: The key to search for
1095
+ * @param list: The list of key-value pairs
1096
+ *
1097
+ * @returns `Some(value)` if the key is found or `None` otherwise
1098
+ *
1099
+ * @example
1100
+ * let data = [
1101
+ * ("name", "Alice"),
1102
+ * ("name", "Bob"),
1103
+ * ("age", "30"),
1104
+ * ]
1105
+ * assert List.Associative.get("name", data) == Some("Alice")
1106
+ *
1107
+ * @example List.Associative.get("age", []) == None
1108
+ *
1109
+ * @since v0.7.0
1110
+ */
1111
+ provide let rec get = (key, list) => {
1112
+ match (list) {
1113
+ [] => None,
1114
+ [(k, v), ...rest] when key == k => Some(v),
1115
+ [_, ...rest] => get(key, rest),
1116
+ }
1117
+ }
1118
+
1119
+ /**
1120
+ * Retrieves all values in the list of key-value pairs that match the given key.
1121
+ *
1122
+ * @param key: The key to search for
1123
+ * @param list: The list of key-value pairs
1124
+ *
1125
+ * @returns An array of values matching the given key
1126
+ *
1127
+ * @example
1128
+ * let data = [
1129
+ * ("name", "Alice"),
1130
+ * ("name", "Bob"),
1131
+ * ("age", "30"),
1132
+ * ]
1133
+ * assert List.Associative.getAll("name", data) == [
1134
+ * "Alice",
1135
+ * "Bob"
1136
+ * ]
1137
+ *
1138
+ * @example List.Associative.getAll("age", []) == []
1139
+ *
1140
+ * @since v0.7.0
1141
+ */
1142
+ provide let rec getAll = (key, list) => {
1143
+ match (list) {
1144
+ [] => [],
1145
+ [(k, v), ...rest] when key == k => [v, ...getAll(key, rest)],
1146
+ [_, ...rest] => getAll(key, rest),
1147
+ }
1148
+ }
1149
+
1150
+ /**
1151
+ * Creates a new list with the first value in the list of key-value pairs that matches the key replaced.
1152
+ * If the key is not found the item is appended to the list.
1153
+ *
1154
+ * @param key: The key to replace
1155
+ * @param value: The new value to set
1156
+ * @param list: The list of key-value pairs
1157
+ *
1158
+ * @returns A new list with the key-value pair replaced
1159
+ *
1160
+ * @example
1161
+ * let data = [
1162
+ * ("name", "Alice"),
1163
+ * ("name", "Bob"),
1164
+ * ("age", "30"),
1165
+ * ]
1166
+ * assert List.Associative.set("name", "Charlie", data) == [("name", "Charlie"), ("name", "Bob"), ("age", "30")]
1167
+ *
1168
+ * @example List.Associative.set("age", "30", [("name", "Alice")]) == [("name", "Alice"), ("age", "30")]
1169
+ *
1170
+ * @since v0.7.0
1171
+ */
1172
+ provide let rec set = (key, value, list) => {
1173
+ match (list) {
1174
+ [] => [(key, value)],
1175
+ [(k, _), ...rest] when key == k => [(k, value), ...rest],
1176
+ [first, ...rest] => [first, ...set(key, value, rest)],
1177
+ }
1178
+ }
1179
+
1180
+ let rec setAllHelp = (key, value, list, hitMatch=false) => {
1181
+ match (list) {
1182
+ [] when !hitMatch => [(key, value)],
1183
+ [] => [],
1184
+ [(k, _), ...rest] when key == k =>
1185
+ [(k, value), ...setAllHelp(key, value, rest, hitMatch=true)],
1186
+ [first, ...rest] =>
1187
+ [first, ...setAllHelp(key, value, rest, hitMatch=hitMatch)],
1188
+ }
1189
+ }
1190
+ /**
1191
+ * Creates a new list with all values in the list of key-value pairs that match the key replaced.
1192
+ * If the key is not found the item is appended to the list.
1193
+ *
1194
+ * @param key: The key to replace
1195
+ * @param value: The new value to set
1196
+ * @param list: The list of key-value pairs
1197
+ *
1198
+ * @returns A new list with the key-value pairs replaced
1199
+ *
1200
+ * @example
1201
+ * let data = [
1202
+ * ("name", "Alice"),
1203
+ * ("name", "Bob"),
1204
+ * ("age", "30"),
1205
+ * ]
1206
+ * assert List.Associative.setAll("name", "Charlie", data) == [("name", "Charlie"), ("name", "Charlie"), ("age", "30")]
1207
+ *
1208
+ * @example List.Associative.setAll("age", "30", [("name", "Alice")]) == [("name", "Alice"), ("age", "30")]
1209
+ *
1210
+ * @since v0.7.0
1211
+ */
1212
+ provide let setAll = (key, value, list) => setAllHelp(key, value, list)
1213
+
1214
+ /**
1215
+ * Creates a new list with the first value in the list of key-value pairs that matches the key removed.
1216
+ * If the key is not found, the list is returned unchanged.
1217
+ *
1218
+ * @param key: The key to remove
1219
+ * @param list: The list of key-value pairs
1220
+ *
1221
+ * @returns The new list with the key-value pair removed
1222
+ *
1223
+ * @example
1224
+ * let data = [
1225
+ * ("name", "Alice"),
1226
+ * ("name", "Bob"),
1227
+ * ("age", "30"),
1228
+ * ]
1229
+ * assert List.Associative.remove("name", data) == [("name", "Bob"), ("age", "30")]
1230
+ *
1231
+ * @example List.Associative.remove("age", [("name", "Alice")]) == []
1232
+ *
1233
+ * @since v0.7.0
1234
+ */
1235
+ provide let rec remove = (key, list) => {
1236
+ match (list) {
1237
+ [] => [],
1238
+ [(k, v), ...rest] when key == k => rest,
1239
+ [first, ...rest] => [first, ...remove(key, rest)],
1240
+ }
1241
+ }
1242
+
1243
+ /**
1244
+ * Creates a new list with all values in the list of key-value pairs matching the key removed.
1245
+ * If the key is not found, the list is returned unchanged.
1246
+ *
1247
+ * @param key: The key to remove
1248
+ * @param list: The list of key-value pairs
1249
+ *
1250
+ * @returns The new list with the key-value pairs removed
1251
+ *
1252
+ * @example
1253
+ * let data = [
1254
+ * ("name", "Alice"),
1255
+ * ("name", "Bob"),
1256
+ * ("age", "30"),
1257
+ * ]
1258
+ * assert List.Associative.removeAll("name", data) == [("age", "30")]
1259
+ *
1260
+ * @example List.Associative.removeAll("age", [("name", "Alice")]) == [("name", "Alice")]
1261
+ *
1262
+ * @since v0.7.0
1263
+ */
1264
+ provide let rec removeAll = (key, list) => {
1265
+ match (list) {
1266
+ [] => [],
1267
+ [(k, v), ...rest] when key == k => removeAll(key, rest),
1268
+ [first, ...rest] => [first, ...removeAll(key, rest)],
1269
+ }
1270
+ }
1271
+ }