Skip to content

Commit fbb08bf

Browse files
feat(stdlib): List.Associative Submodule (#2202)
Co-authored-by: Oscar Spencer <oscar.spen@gmail.com>
1 parent b815bcd commit fbb08bf

File tree

3 files changed

+341
-0
lines changed

3 files changed

+341
-0
lines changed

compiler/test/stdlib/list.test.gr

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,3 +289,31 @@ assert sort(compare=compareLengths, list) ==
289289
["a", "a", "ab", "abc", "abcd", "abcde"]
290290
assert sort(compare=compareLengths, ["a", "a", "a", "a"]) ==
291291
["a", "a", "a", "a"]
292+
293+
// List.Associative
294+
module Associative {
295+
let data = [("name", "Alice"), ("name", "Bob"), ("age", "30")]
296+
297+
// List.Associative.has
298+
assert List.Associative.has("name", data)
299+
assert !List.Associative.has("age", [])
300+
301+
// List.Associative.get
302+
assert List.Associative.get("name", data) == Some("Alice")
303+
assert List.Associative.get("age", []) == None
304+
assert List.Associative.get("age", data) == Some("30")
305+
306+
// List.Associative.set
307+
assert List.Associative.set("name", "Charlie", data) ==
308+
[("name", "Charlie"), ("name", "Bob"), ("age", "30")]
309+
assert List.Associative.set("age", "31", data) ==
310+
[("name", "Alice"), ("name", "Bob"), ("age", "31")]
311+
assert List.Associative.set("age", "31", []) == [("age", "31")]
312+
313+
// List.Associative.remove
314+
assert List.Associative.remove("name", data) ==
315+
[("name", "Bob"), ("age", "30")]
316+
assert List.Associative.remove("age", data) ==
317+
[("name", "Alice"), ("name", "Bob")]
318+
assert List.Associative.remove("age", []) == []
319+
}

stdlib/list.gr

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,3 +1047,129 @@ provide let sort = (compare=compare, list) => {
10471047

10481048
mergesort(list)
10491049
}
1050+
1051+
/**
1052+
* Utilities for working with lists of key-key value pairs.
1053+
*
1054+
* @example
1055+
* let data = [
1056+
* ("name", "Alice"),
1057+
* ("age", "30"),
1058+
* ]
1059+
* assert List.Associative.get("name", data) == Some("Alice")
1060+
*
1061+
* @since v0.7.0
1062+
*/
1063+
provide module Associative {
1064+
/**
1065+
* Checks if the given key is present in the list of key-value pairs.
1066+
*
1067+
* @param key: The key to search for
1068+
* @param list: The list of key-value pairs
1069+
*
1070+
* @returns `true` if the key is found or `false` otherwise
1071+
*
1072+
* @example
1073+
* let data = [
1074+
* ("name", "Alice"),
1075+
* ("age", "30"),
1076+
* ]
1077+
* assert List.Associative.has("name", data) == true
1078+
* @example List.Associative.has("age", []) == false
1079+
*
1080+
* @since v0.7.0
1081+
*/
1082+
provide let rec has = (key, list) => {
1083+
match (list) {
1084+
[] => false,
1085+
[(k, _), ...rest] when key == k => true,
1086+
[_, ...rest] => has(key, rest),
1087+
}
1088+
}
1089+
/**
1090+
* Retrieves the first value in the list of key-value pairs that matches the given key.
1091+
*
1092+
* @param key: The key to search for
1093+
* @param list: The list of key-value pairs
1094+
*
1095+
* @returns `Some(value)` if the key is found or `None` otherwise
1096+
*
1097+
* @example
1098+
* let data = [
1099+
* ("name", "Alice"),
1100+
* ("name", "Bob"),
1101+
* ("age", "30"),
1102+
* ]
1103+
* assert List.Associative.get("name", data) == Some("Alice")
1104+
*
1105+
* @example List.Associative.get("age", []) == None
1106+
*
1107+
* @since v0.7.0
1108+
*/
1109+
provide let rec get = (key, list) => {
1110+
match (list) {
1111+
[] => None,
1112+
[(k, v), ...rest] when key == k => Some(v),
1113+
[_, ...rest] => get(key, rest),
1114+
}
1115+
}
1116+
1117+
/**
1118+
* Creates a new list with the first value in the list of key-value pairs that matches the key replaced.
1119+
* If the key is not found the item is appended to the list.
1120+
*
1121+
* @param key: The key to replace
1122+
* @param value: The new value to set
1123+
* @param list: The list of key-value pairs
1124+
*
1125+
* @returns A new list with the key-value pair replaced
1126+
*
1127+
* @example
1128+
* let data = [
1129+
* ("name", "Alice"),
1130+
* ("name", "Bob"),
1131+
* ("age", "30"),
1132+
* ]
1133+
* assert List.Associative.set("name", "Charlie", data) == [("name", "Charlie"), ("name", "Bob"), ("age", "30")]
1134+
*
1135+
* @example List.Associative.set("age", "30", [("name", "Alice")]) == [("name", "Alice"), ("age", "30")]
1136+
*
1137+
* @since v0.7.0
1138+
*/
1139+
provide let rec set = (key, value, list) => {
1140+
match (list) {
1141+
[] => [(key, value)],
1142+
[(k, _), ...rest] when key == k => [(k, value), ...rest],
1143+
[first, ...rest] => [first, ...set(key, value, rest)],
1144+
}
1145+
}
1146+
1147+
/**
1148+
* Creates a new list with the first value in the list of key-value pairs that matches the key removed.
1149+
* If the key is not found, the list is returned unchanged.
1150+
*
1151+
* @param key: The key to remove
1152+
* @param list: The list of key-value pairs
1153+
*
1154+
* @returns The new list with the key-value pair removed
1155+
*
1156+
* @example
1157+
* let data = [
1158+
* ("name", "Alice"),
1159+
* ("name", "Bob"),
1160+
* ("age", "30"),
1161+
* ]
1162+
* assert List.Associative.remove("name", data) == [("name", "Bob"), ("age", "30")]
1163+
*
1164+
* @example List.Associative.remove("age", [("name", "Alice")]) == []
1165+
*
1166+
* @since v0.7.0
1167+
*/
1168+
provide let rec remove = (key, list) => {
1169+
match (list) {
1170+
[] => [],
1171+
[(k, v), ...rest] when key == k => rest,
1172+
[first, ...rest] => [first, ...remove(key, rest)],
1173+
}
1174+
}
1175+
}

stdlib/list.md

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,3 +1430,190 @@ Returns:
14301430
|----|-----------|
14311431
|`List<a>`|The sorted list|
14321432

1433+
## List.Associative
1434+
1435+
Utilities for working with lists of key-key value pairs.
1436+
1437+
<details disabled>
1438+
<summary tabindex="-1">Added in <code>next</code></summary>
1439+
No other changes yet.
1440+
</details>
1441+
1442+
```grain
1443+
let data = [
1444+
("name", "Alice"),
1445+
("age", "30"),
1446+
]
1447+
assert List.Associative.get("name", data) == Some("Alice")
1448+
```
1449+
1450+
### Values
1451+
1452+
Functions and constants included in the List.Associative module.
1453+
1454+
#### List.Associative.**has**
1455+
1456+
<details disabled>
1457+
<summary tabindex="-1">Added in <code>next</code></summary>
1458+
No other changes yet.
1459+
</details>
1460+
1461+
```grain
1462+
has : (key: a, list: List<(a, b)>) => Bool
1463+
```
1464+
1465+
Checks if the given key is present in the list of key-value pairs.
1466+
1467+
Parameters:
1468+
1469+
|param|type|description|
1470+
|-----|----|-----------|
1471+
|`key`|`a`|The key to search for|
1472+
|`list`|`List<(a, b)>`|The list of key-value pairs|
1473+
1474+
Returns:
1475+
1476+
|type|description|
1477+
|----|-----------|
1478+
|`Bool`|`true` if the key is found or `false` otherwise|
1479+
1480+
Examples:
1481+
1482+
```grain
1483+
let data = [
1484+
("name", "Alice"),
1485+
("age", "30"),
1486+
]
1487+
assert List.Associative.has("name", data) == true
1488+
```
1489+
1490+
```grain
1491+
List.Associative.has("age", []) == false
1492+
```
1493+
1494+
#### List.Associative.**get**
1495+
1496+
<details disabled>
1497+
<summary tabindex="-1">Added in <code>next</code></summary>
1498+
No other changes yet.
1499+
</details>
1500+
1501+
```grain
1502+
get : (key: a, list: List<(a, b)>) => Option<b>
1503+
```
1504+
1505+
Retrieves the first value in the list of key-value pairs that matches the given key.
1506+
1507+
Parameters:
1508+
1509+
|param|type|description|
1510+
|-----|----|-----------|
1511+
|`key`|`a`|The key to search for|
1512+
|`list`|`List<(a, b)>`|The list of key-value pairs|
1513+
1514+
Returns:
1515+
1516+
|type|description|
1517+
|----|-----------|
1518+
|`Option<b>`|`Some(value)` if the key is found or `None` otherwise|
1519+
1520+
Examples:
1521+
1522+
```grain
1523+
let data = [
1524+
("name", "Alice"),
1525+
("name", "Bob"),
1526+
("age", "30"),
1527+
]
1528+
assert List.Associative.get("name", data) == Some("Alice")
1529+
```
1530+
1531+
```grain
1532+
List.Associative.get("age", []) == None
1533+
```
1534+
1535+
#### List.Associative.**set**
1536+
1537+
<details disabled>
1538+
<summary tabindex="-1">Added in <code>next</code></summary>
1539+
No other changes yet.
1540+
</details>
1541+
1542+
```grain
1543+
set : (key: a, value: b, list: List<(a, b)>) => List<(a, b)>
1544+
```
1545+
1546+
Creates a new list with the first value in the list of key-value pairs that matches the key replaced.
1547+
If the key is not found the item is appended to the list.
1548+
1549+
Parameters:
1550+
1551+
|param|type|description|
1552+
|-----|----|-----------|
1553+
|`key`|`a`|The key to replace|
1554+
|`value`|`b`|The new value to set|
1555+
|`list`|`List<(a, b)>`|The list of key-value pairs|
1556+
1557+
Returns:
1558+
1559+
|type|description|
1560+
|----|-----------|
1561+
|`List<(a, b)>`|A new list with the key-value pair replaced|
1562+
1563+
Examples:
1564+
1565+
```grain
1566+
let data = [
1567+
("name", "Alice"),
1568+
("name", "Bob"),
1569+
("age", "30"),
1570+
]
1571+
assert List.Associative.set("name", "Charlie", data) == [("name", "Charlie"), ("name", "Bob"), ("age", "30")]
1572+
```
1573+
1574+
```grain
1575+
List.Associative.set("age", "30", [("name", "Alice")]) == [("name", "Alice"), ("age", "30")]
1576+
```
1577+
1578+
#### List.Associative.**remove**
1579+
1580+
<details disabled>
1581+
<summary tabindex="-1">Added in <code>next</code></summary>
1582+
No other changes yet.
1583+
</details>
1584+
1585+
```grain
1586+
remove : (key: a, list: List<(a, b)>) => List<(a, b)>
1587+
```
1588+
1589+
Creates a new list with the first value in the list of key-value pairs that matches the key removed.
1590+
If the key is not found, the list is returned unchanged.
1591+
1592+
Parameters:
1593+
1594+
|param|type|description|
1595+
|-----|----|-----------|
1596+
|`key`|`a`|The key to remove|
1597+
|`list`|`List<(a, b)>`|The list of key-value pairs|
1598+
1599+
Returns:
1600+
1601+
|type|description|
1602+
|----|-----------|
1603+
|`List<(a, b)>`|The new list with the key-value pair removed|
1604+
1605+
Examples:
1606+
1607+
```grain
1608+
let data = [
1609+
("name", "Alice"),
1610+
("name", "Bob"),
1611+
("age", "30"),
1612+
]
1613+
assert List.Associative.remove("name", data) == [("name", "Bob"), ("age", "30")]
1614+
```
1615+
1616+
```grain
1617+
List.Associative.remove("age", [("name", "Alice")]) == []
1618+
```
1619+

0 commit comments

Comments
 (0)