Skip to content

Commit

Permalink
feat(stdlib): List.Associative Submodule (#2202)
Browse files Browse the repository at this point in the history
Co-authored-by: Oscar Spencer <[email protected]>
  • Loading branch information
spotandjake and ospencer authored Jan 3, 2025
1 parent b815bcd commit fbb08bf
Show file tree
Hide file tree
Showing 3 changed files with 341 additions and 0 deletions.
28 changes: 28 additions & 0 deletions compiler/test/stdlib/list.test.gr
Original file line number Diff line number Diff line change
Expand Up @@ -289,3 +289,31 @@ assert sort(compare=compareLengths, list) ==
["a", "a", "ab", "abc", "abcd", "abcde"]
assert sort(compare=compareLengths, ["a", "a", "a", "a"]) ==
["a", "a", "a", "a"]

// List.Associative
module Associative {
let data = [("name", "Alice"), ("name", "Bob"), ("age", "30")]

// List.Associative.has
assert List.Associative.has("name", data)
assert !List.Associative.has("age", [])

// List.Associative.get
assert List.Associative.get("name", data) == Some("Alice")
assert List.Associative.get("age", []) == None
assert List.Associative.get("age", data) == Some("30")

// List.Associative.set
assert List.Associative.set("name", "Charlie", data) ==
[("name", "Charlie"), ("name", "Bob"), ("age", "30")]
assert List.Associative.set("age", "31", data) ==
[("name", "Alice"), ("name", "Bob"), ("age", "31")]
assert List.Associative.set("age", "31", []) == [("age", "31")]

// List.Associative.remove
assert List.Associative.remove("name", data) ==
[("name", "Bob"), ("age", "30")]
assert List.Associative.remove("age", data) ==
[("name", "Alice"), ("name", "Bob")]
assert List.Associative.remove("age", []) == []
}
126 changes: 126 additions & 0 deletions stdlib/list.gr
Original file line number Diff line number Diff line change
Expand Up @@ -1047,3 +1047,129 @@ provide let sort = (compare=compare, list) => {

mergesort(list)
}

/**
* Utilities for working with lists of key-key value pairs.
*
* @example
* let data = [
* ("name", "Alice"),
* ("age", "30"),
* ]
* assert List.Associative.get("name", data) == Some("Alice")
*
* @since v0.7.0
*/
provide module Associative {
/**
* Checks if the given key is present in the list of key-value pairs.
*
* @param key: The key to search for
* @param list: The list of key-value pairs
*
* @returns `true` if the key is found or `false` otherwise
*
* @example
* let data = [
* ("name", "Alice"),
* ("age", "30"),
* ]
* assert List.Associative.has("name", data) == true
* @example List.Associative.has("age", []) == false
*
* @since v0.7.0
*/
provide let rec has = (key, list) => {
match (list) {
[] => false,
[(k, _), ...rest] when key == k => true,
[_, ...rest] => has(key, rest),
}
}
/**
* Retrieves the first value in the list of key-value pairs that matches the given key.
*
* @param key: The key to search for
* @param list: The list of key-value pairs
*
* @returns `Some(value)` if the key is found or `None` otherwise
*
* @example
* let data = [
* ("name", "Alice"),
* ("name", "Bob"),
* ("age", "30"),
* ]
* assert List.Associative.get("name", data) == Some("Alice")
*
* @example List.Associative.get("age", []) == None
*
* @since v0.7.0
*/
provide let rec get = (key, list) => {
match (list) {
[] => None,
[(k, v), ...rest] when key == k => Some(v),
[_, ...rest] => get(key, rest),
}
}

/**
* Creates a new list with the first value in the list of key-value pairs that matches the key replaced.
* If the key is not found the item is appended to the list.
*
* @param key: The key to replace
* @param value: The new value to set
* @param list: The list of key-value pairs
*
* @returns A new list with the key-value pair replaced
*
* @example
* let data = [
* ("name", "Alice"),
* ("name", "Bob"),
* ("age", "30"),
* ]
* assert List.Associative.set("name", "Charlie", data) == [("name", "Charlie"), ("name", "Bob"), ("age", "30")]
*
* @example List.Associative.set("age", "30", [("name", "Alice")]) == [("name", "Alice"), ("age", "30")]
*
* @since v0.7.0
*/
provide let rec set = (key, value, list) => {
match (list) {
[] => [(key, value)],
[(k, _), ...rest] when key == k => [(k, value), ...rest],
[first, ...rest] => [first, ...set(key, value, rest)],
}
}

/**
* Creates a new list with the first value in the list of key-value pairs that matches the key removed.
* If the key is not found, the list is returned unchanged.
*
* @param key: The key to remove
* @param list: The list of key-value pairs
*
* @returns The new list with the key-value pair removed
*
* @example
* let data = [
* ("name", "Alice"),
* ("name", "Bob"),
* ("age", "30"),
* ]
* assert List.Associative.remove("name", data) == [("name", "Bob"), ("age", "30")]
*
* @example List.Associative.remove("age", [("name", "Alice")]) == []
*
* @since v0.7.0
*/
provide let rec remove = (key, list) => {
match (list) {
[] => [],
[(k, v), ...rest] when key == k => rest,
[first, ...rest] => [first, ...remove(key, rest)],
}
}
}
187 changes: 187 additions & 0 deletions stdlib/list.md
Original file line number Diff line number Diff line change
Expand Up @@ -1430,3 +1430,190 @@ Returns:
|----|-----------|
|`List<a>`|The sorted list|

## List.Associative

Utilities for working with lists of key-key value pairs.

<details disabled>
<summary tabindex="-1">Added in <code>next</code></summary>
No other changes yet.
</details>

```grain
let data = [
("name", "Alice"),
("age", "30"),
]
assert List.Associative.get("name", data) == Some("Alice")
```

### Values

Functions and constants included in the List.Associative module.

#### List.Associative.**has**

<details disabled>
<summary tabindex="-1">Added in <code>next</code></summary>
No other changes yet.
</details>

```grain
has : (key: a, list: List<(a, b)>) => Bool
```

Checks if the given key is present in the list of key-value pairs.

Parameters:

|param|type|description|
|-----|----|-----------|
|`key`|`a`|The key to search for|
|`list`|`List<(a, b)>`|The list of key-value pairs|

Returns:

|type|description|
|----|-----------|
|`Bool`|`true` if the key is found or `false` otherwise|

Examples:

```grain
let data = [
("name", "Alice"),
("age", "30"),
]
assert List.Associative.has("name", data) == true
```

```grain
List.Associative.has("age", []) == false
```

#### List.Associative.**get**

<details disabled>
<summary tabindex="-1">Added in <code>next</code></summary>
No other changes yet.
</details>

```grain
get : (key: a, list: List<(a, b)>) => Option<b>
```

Retrieves the first value in the list of key-value pairs that matches the given key.

Parameters:

|param|type|description|
|-----|----|-----------|
|`key`|`a`|The key to search for|
|`list`|`List<(a, b)>`|The list of key-value pairs|

Returns:

|type|description|
|----|-----------|
|`Option<b>`|`Some(value)` if the key is found or `None` otherwise|

Examples:

```grain
let data = [
("name", "Alice"),
("name", "Bob"),
("age", "30"),
]
assert List.Associative.get("name", data) == Some("Alice")
```

```grain
List.Associative.get("age", []) == None
```

#### List.Associative.**set**

<details disabled>
<summary tabindex="-1">Added in <code>next</code></summary>
No other changes yet.
</details>

```grain
set : (key: a, value: b, list: List<(a, b)>) => List<(a, b)>
```

Creates a new list with the first value in the list of key-value pairs that matches the key replaced.
If the key is not found the item is appended to the list.

Parameters:

|param|type|description|
|-----|----|-----------|
|`key`|`a`|The key to replace|
|`value`|`b`|The new value to set|
|`list`|`List<(a, b)>`|The list of key-value pairs|

Returns:

|type|description|
|----|-----------|
|`List<(a, b)>`|A new list with the key-value pair replaced|

Examples:

```grain
let data = [
("name", "Alice"),
("name", "Bob"),
("age", "30"),
]
assert List.Associative.set("name", "Charlie", data) == [("name", "Charlie"), ("name", "Bob"), ("age", "30")]
```

```grain
List.Associative.set("age", "30", [("name", "Alice")]) == [("name", "Alice"), ("age", "30")]
```

#### List.Associative.**remove**

<details disabled>
<summary tabindex="-1">Added in <code>next</code></summary>
No other changes yet.
</details>

```grain
remove : (key: a, list: List<(a, b)>) => List<(a, b)>
```

Creates a new list with the first value in the list of key-value pairs that matches the key removed.
If the key is not found, the list is returned unchanged.

Parameters:

|param|type|description|
|-----|----|-----------|
|`key`|`a`|The key to remove|
|`list`|`List<(a, b)>`|The list of key-value pairs|

Returns:

|type|description|
|----|-----------|
|`List<(a, b)>`|The new list with the key-value pair removed|

Examples:

```grain
let data = [
("name", "Alice"),
("name", "Bob"),
("age", "30"),
]
assert List.Associative.remove("name", data) == [("name", "Bob"), ("age", "30")]
```

```grain
List.Associative.remove("age", [("name", "Alice")]) == []
```

0 comments on commit fbb08bf

Please sign in to comment.