Skip to content

Commit 43d8407

Browse files
committed
Add nested-map support in sass:map module
1 parent b7968bc commit 43d8407

File tree

1 file changed

+204
-0
lines changed

1 file changed

+204
-0
lines changed

proposal/nested-map-functions.md

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
# Nested Map Functions: Draft 1.0
2+
3+
*[(Issue)](https://github.com/sass/sass/issues/1739)*
4+
5+
This proposal updates the built-in `sass:map` module to better support merging,
6+
setting, and getting items from nested maps.
7+
8+
## Table of Contents
9+
10+
* [Background](#background)
11+
* [Summary](#summary)
12+
* [Syntax](#syntax)
13+
* [`get()`](#get)
14+
* [`has-key()`](#has-key)
15+
* [`set()`](#set)
16+
* [`merge()`](#merge)
17+
18+
## Background
19+
20+
> This section is non-normative.
21+
22+
The current map inspection and manipulation functions don't provide any built-in
23+
support for managing nested maps. Projects often build thir own tooling, but
24+
the results are inconsistent, and often slow.
25+
26+
## Summary
27+
28+
> This section is non-normative.
29+
30+
This proposal updates the `sass:map` module to better support inspection and
31+
manipulation of nested maps.
32+
33+
The `has-key()` and `get()` functions both accept multiple `$keys...`:
34+
35+
```scss
36+
@use 'sass:map';
37+
38+
$nav: (
39+
'bg': 'gray',
40+
'color': (
41+
'hover': (
42+
'search': yellow,
43+
'home': red,
44+
'filter': blue,
45+
),
46+
),
47+
);
48+
49+
$has-search: map.has-key($nav, 'color', 'hover', 'search'); // true
50+
$search-hover: map.get($nav, 'color', 'hover', 'search'); // yellow
51+
```
52+
53+
The `merge()` function now accepts multiple `$keys...` between the two maps
54+
being merged. The keys form a path to the nested location in `$map1` where
55+
`$map2` should be merged. For example, we update the hover colors in our `$nav`
56+
map above:
57+
58+
```scss
59+
@use 'sass:map';
60+
61+
$new-hover: (
62+
'search': green,
63+
'logo': orange,
64+
);
65+
66+
$nav: map.merge($nav, 'color', 'hover', $new-hover);
67+
68+
// $nav: (
69+
// 'bg': 'gray',
70+
// 'color': (
71+
// 'hover': (
72+
// 'search': green,
73+
// 'home': red,
74+
// 'filter': blue,
75+
// 'logo': orange,
76+
// ),
77+
// ),
78+
// );
79+
```
80+
81+
This proposal also adds a `set()` function to `sass:map`, with a similar syntax,
82+
returning a map with any nested key set to a specific value. To achieve the
83+
same output as our merge example, we can set each key individually:
84+
85+
```scss
86+
@use 'sass:map';
87+
88+
$nav: map.set($nav, 'color', 'hover', 'search', green);
89+
$nav: map.set($nav, 'color', 'hover', 'logo', orange);
90+
```
91+
92+
## Syntax
93+
94+
### `get()`
95+
96+
```
97+
get($map, $keys...)
98+
```
99+
100+
* Let `key` be the first (or only) element in `$keys`
101+
102+
* If `$map` does not have a key with the same name as `key`, throw an error.
103+
104+
* Let `value` be the value assigned to `key` in `$map`
105+
106+
* If there is more than one element in `$keys`:
107+
108+
* Let `keys` be all elements in `$keys` after the first element
109+
110+
* Call `get()` with `value` and expanded `keys` as arguments
111+
112+
* Otherwise, return `value`
113+
114+
### `has-key()`
115+
116+
```
117+
has-key($map, $keys...)
118+
```
119+
120+
* Let `key` be the first (or only) element in `$keys`
121+
122+
* If `$map` does not have a key with the same name as `key`, return boolean
123+
`false`.
124+
125+
* If there is more than one element in `$keys`:
126+
127+
* Let `map` be the value assigned to `key` in `$map`
128+
129+
* Let `keys` be all but the first element in `$keys`
130+
131+
* Call `has-key()` with `map` and expanded `keys` as arguments
132+
133+
* Otherwise, return boolean `true`
134+
135+
### `set()`
136+
137+
```
138+
set($map1, $keys..., $value)
139+
```
140+
141+
* If `$map1` is not a map, throw an error.
142+
143+
* If fewer than three arguments are provided, throw an error.
144+
145+
* Let `map` be an empty map .
146+
147+
* If there is more than one argument in arglist `$keys`:
148+
149+
* Let `set-key` be the first element in `$keys`.
150+
151+
* Let `keys` be a slice of all `$keys` elements except the first.
152+
153+
* If `$map1` has a key `current-key` with the same name as `set-key`:
154+
155+
* Let `current` be the value of `current-key`.
156+
157+
* Otherwise:
158+
159+
* Let `current` be an empty map.
160+
161+
* Let `set-value` be the result of calling `set()` with `current` and expanded
162+
`keys` as arguments.
163+
164+
> This will error if `current` is not a map, but we stil have nested `keys`
165+
166+
* Otherwise:
167+
168+
* Let `set-key` be the only element in `$keys`
169+
170+
* Let `set-value` be the value of `$value`
171+
172+
* Add a key with name `set-key` and value `set-value` to `map`
173+
174+
* For each `key`, `value` pair in `$map1`
175+
176+
* If a key with name `key` already exists in `map`, do nothing.
177+
178+
* Otherwise, add a key to `map` with name `key` and value `value`.
179+
180+
* Return the value `map`
181+
182+
### `merge()`
183+
184+
```
185+
merge($map1, $keys..., $map2)
186+
```
187+
188+
* If only one argument is provided, throw an error.
189+
190+
* If either the first (`$map1`) or last (`$map2`) argument is not a map, throw
191+
an error.
192+
193+
* Let `map` be an empty map.
194+
195+
* For each `key`, `new` pair in `$map2`:
196+
197+
* If `$map1` has a key with the same name as `key`
198+
199+
* Let `keys` be the result of appending `key` to the argument list `$keys`.
200+
201+
* Update `map` to be the result of calling `set()` with `map`,
202+
expanded `keys`, and `value` as arguments.
203+
204+
* Return the value of `map`

0 commit comments

Comments
 (0)