Request for Comments: Nested Map Functions
Posted 16 September 2020 by Natalie Weizenbaum
As Sass libraries and design systems get more complex and have more users with different needs, they tend to develop the need to share and override configuration and design tokens. This configuration is often hierarchical, and ends up being represented as maps that contain maps that contain still more maps. Up until now, Sass’s map functions haven’t really made it easy to work with this sort of nested map structure. But that’s changing with the latest language proposal, written by Sass core team member Miriam Suzanne.
This proposal expands the existing map functions and adds a few new ones to make working with nested maps much easier than it was before. It’s based on helper functions that pop up in all sorts of Sass projects around the web, incorporating best practices back into the language itself.
The FunctionsThe Functions permalink
Here are the new and improved functions this proposal adds:
map.has-key()map.get() and map.has-key() permalink
map.has-key() functions both now take any number of
keys as arguments. Each key drills deeper into a nested map, allowing you to
easily inspect nested values without needing to chain a bunch of function calls together.
For example, let’s take the following simplified configuration map:
$config: ( "colors": ( "primary": red, "secondary": blue ) )
For this map,
map.get($config, "colors", "primary") gets the value of the
"colors" key (
("primary": red)) then the value of the
"primary" key and
map.has-key($config, "colors", "primary") returns
map.has-key($config, "colors", "tertiary") returns
map.merge() function can now be called as
map.merge($map1, $keys..., $map2). This will merge
$map2 with a child of
$map1 at the location given
by the keys, updating the parent maps as it goes.
For example, using the configuration map defined above
map.merge($config, "colors", ("primary": green)) will return
( "colors": ( "primary": green, "secondary": blue ) )
map.set($map, $keys..., $value) function is all-new. Although updating
individual values in maps was always possible with
map.merge(), we’ve found
that users get confused by the absence of a dedicated
set() function. This
function not only fills that role, but makes it possible to set values within
nested maps as well.
You can use
map.set() for normal single-layer maps by just passing one key.
map.set(("wide": 200px, "narrow": 70px), "wide", 180px) will
("wide": 180px, "narrow": 70px).
But you can also use it for nested maps. For example,
map.set($config, "colors", "tertiary", yellow) will return
( "colors": ( "primary": red, "secondary": blue, "tertiary": yellow ) )
Because the existing
map.remove() function already takes any number of
arguments, we couldn’t just update it to work with nested maps. Instead, we
chose to add a new function just for nested maps, called
map.deep-remove($map, $keys...). This function removes the value at the final key in the list, and
updates all the parent maps accordingly.
map.deep-remove($config, "colors", "secondary") will return
("colors": ("primary": red)).
The final new function may be the most exciting.
works just like
map.merge(), except that any nested maps are also merged,
including maps within those maps and so on. This makes it easy to combine two
configuration maps that have the same structure without having to manually merge
each level by hand.
map.deep-merge($config, ("colors": ("secondary": teal))) returns
( "colors": ( "primary": red, "secondary": teal ) )
Let us know what you think!Let us know what you think! permalink
If you’re interested in learning more about this proposal, read it in full on GitHub. It’s open for comments and revisions for the next month, so if you’d like to see something change please file an issue and we can discuss it!