Tuesday, 23 August 2016

Building Better Data Structures With Sass Maps - Vanseo Design

Building Better Data Structures With Sass Maps - Vanseo Design


Building Better Data Structures With Sass Maps

Posted: 23 Aug 2016 05:30 AM PDT

One downside of working with lists in Sass is you have to access values by an index you may not know. Maps solve that problem and allow you to access values based on key names you assign to them.

Map showing Italy

I've been talking about data types, operators, and functions for awhile now, having covered numbers, strings, colors, colors again, lists and list functions. This post brings us to the last data type, maps.

Maps are similar to lists, but they take lists one better. Instead of values being stored only with a numbered index, each value is associated with a key name. That allows you to store data in a way that makes it easier to retrieve and use.

Sass Maps

Where Sass lists are similar to the generic arrays of most programming languages, Sass maps are similar to associative arrays. They allow you to associate every value with a keyword or key name so the value can be referenced by name instead of by a numbered index.

When you declare a map you declare key: value pairs as opposed to values only.

1  
$map: (key1: value1, key2: value2, key3: value3);

Unlike lists, the parenthesis are necessary and you must separate each key: value pair with a comma. You can, however, use multiple lines and you'll likely want to for maps containing more than a few key: value pairs.

1  2  3  4  5  
$map: (      key1: value1,      key2: value2,      key3: value3    );

The keys and values can be any object in Sass and while each key can only have a single value associated with it, the values can be lists of values. You can also associate any value with more than one key.

Like lists, maps have no special operators associated with them. You use map functions to manipulate maps.

Maps can be thought of as a special type of list so all maps are also lists. The reverse isn't true. Each key/value pair in a map is treated as a nested list when a map is used in a list function. This means all the list functions can also be used to manipulate maps further increasing they ways you can work with maps.

1  
$map: (key1: value1, key2: value2, key3: value3);

Here key1: value1 becomes one nested list, key2: value2 another and so on. The first index of $map would be the list key1: value1. Each successive index is the next key/value pair interpreted as another nested list.

One downside to maps is they have no CSS equivalent and so you can't directly output a map as CSS. Trying to do so will produce an error. Instead you want to use the inspect($value) function, which converts $value into a string that can be output to CSS. You can, however, directly output the value part of a key/value pair as CSS as we'll see momentarily.

Map Functions

Like lists, maps are immutable. Map functions return a new map rather than changing the existing map. If you want to save a copy of the returned map, you'll need to assign the returned key/value pairs to a new map.

To find the value of a known key you can use the map-get($map, $key) function. In the following example I created a map named $margin with four key/value pairs for margin-top, margin-right, etc. I use the map-get() function to find the value for the margin-right key and then set it as the right margin of an h1.

1  2  3  4  5  6  7  8  9  10  
$margin: (     margin-top: 10px,     margin-right: 20px,     margin-bottom: 30px,     margin-left: 40px    );    h1 {     margin-right: map-get($margin, margin-right);    }

The Sass compiles to:

1  2  3  
h1 {     margin-right: 20px;    }

When using a map in a function or mixin, you may not know the key names in advance. In that case you can get a list of all the keys a map contains with the map-keys($map) function.

1  2  3  4  5  6  7  8  
$margin: (     margin-top: 10px,     margin-right: 20px,     margin-bottom: 30px,     margin-left: 40px    );    map-keys($margin) => margin-top, margin-right, margin-bottom, margin-left

Similarly if you want a list of all the values in a map you can use the map-values($map) function.

1  2  3  4  5  6  7  8  
$margin: (     margin-top: 10px,     margin-right: 20px,     margin-bottom: 30px,     margin-left: 40px    );    map-values($margin) => 10px, 20px, 30px, 40px

Instead of getting a complete list of keys you can test to see if a specific key exists using the map-has-key($map, $key) function.

1  2  3  4  5  6  7  8  9  
$margin: (     margin-top: 10px,     margin-right: 20px,     margin-bottom: 30px,     margin-left: 40px    );    map-has-key($margin, margin-left) => true    map-has-key($margin, margin-side) => false

You aren't limited to reading the keys and values of maps or testing to see if certain keys are present. You can remove key/value pairs with the map-remove($map, $keys…) function.

1  2  3  4  5  6  7  8  
$margin: (     margin-top: 10px,     margin-right: 20px,     margin-bottom: 30px,     margin-left: 40px    );    map-remove($margin, margin-top, margin-bottom) => (margin-right: 20px, margin-left: 40px)

As I mentioned earlier, the returned map is different than the original, if you want to save it you need to assign the returned values to a new map.

1  
$margin-2: map-remove($margin, margin-top, margin-bottom);

$margin–2 would then contain only the remaining two key/value pairs.

1  2  3  4  
$margin-2 (      margin-right: 20px,      margin-left: 40px    );

If you want to add items to a map you can use the map-merge($map1, $map2) function, which combines two maps, adding the second one to the first one.

1  2  3  4  5  6  7  8  9  10  11  12  
$margin-1: (     margin-top: 10px,     margin-right: 20px    );    $margin-2: (     margin-bottom: 30px,     margin-left: 40px    );    $margin-3: map-merge($margin-1, $margin-2);    $margin-4: map-merge($margin-2, $margin-1);

This would lead to the following being stored in $margin-3 and $margin-4.

1  2  3  4  5  6  7  8  9  10  11  12  13  
$margin-3: (     margin-top: 10px,     margin-right: 20px,     margin-bottom: 30px,     margin-left: 40px    );    $margin-3: (     margin-bottom: 30px,     margin-left: 40px,     margin-top: 10px,     margin-right: 20px    );

One last function is the keywords($args) function, which you might use to get a map of named arguments that have been passed to a function or mixin.

In the following example I created a mixin that takes some number of $arguments. I also created a map of $margins that should look familiar by now. Finally I included the $margins map as the $arguments passed to the mix.

1  2  3  4  5  6  7  8  9  10  11  12  
@mixin margin($args...) {      @debug keywords($args);   // (margin-top: 10px, margin-right: 20px, margin-bottom: 30px, margin-left: 40px)    }    $margins: (     margin-top: 10px,     margin-right: 20px,     margin-bottom: 30px,     margin-left: 40px    );    @include margin($margins);

What you should note is the use of keywords($args). @debug prints the results to the standard error output stream and what's on the right side of the // is what would be output.

The function returns the key/value pairs as a string and not a map. Notice that the $ before the values has been removed in the output. I'm not entirely sure when or if you would use the keywords($args) function aside from debugging, but it's there for you to use.

Closing Thoughts

Sass maps are a special kind of nested list, akin to an associative array. They store key/value pairs, which makes it easy to find the values of specific keys, instead of having to know the index of the value you want to access.

Because maps are also lists any list function will work with a map in addition to the functions specific to maps. Each key/value pair is treated as a nested list when using list functions on maps.

At times you might have wondered through this series when would you work with these data types and use the operators and functions Sass provides to manipulate them. The color data type has some built-in functions that you probably found useful, but what about the other data types?

The answer is when you write your own mixins or custom functions, particularly when they include control directives for things like telling your code to do this under these conditions and do that as long as those conditions last. The next couple of weeks I'll look at control directives in Sass and then I'll finish up showing you how to create your own custom functions.

Download a free sample from my book Design Fundamentals.

Join me as I share my creative process and journey as a writer.

This posting includes an audio/video/photo media file: Download Now