Sass Maps vs. Nested Lists

Share this article

The title of this post may be surprising to some of you. If you are a Sass veteran, you may remember the days (pre-Ruby-Sass-3.3) of using lists of lists to emulate nested arrays of data. (Ruby) Sass 3.3 added a new data type called maps. Lists of lists could hold complex data in a nested format, but without key-value pairing. Maps added key-value pairs and let us create arrays of data.

With the advent of maps, many of us Sass users started putting everything into maps (and for good reason!). All your breakpoint widths, color values, grid layouts, type scales and other responsive typography details can go into maps!

Now that Sass has maps with key-value pairs, is there a good reason to use a list of lists? One theoretical reason would be backwards-compatibility: if your Sass might be maintained by a developer with an older version installed, lists will help them. In practice, however, Sass versions are often controlled by a package.json or other project config, and the Ruby gem can be updated with just one command (gem update sass).

A more practical reason that you might choose to use nested lists instead of a map is typing less. Let’s compare a map and a nested list to see how they compare in their own syntax, and in how we’d loop through them.

Comparing Syntax

For our example, let’s create a data structure that controls responsive typography. It will store four breakpoints (well, one is the default smallest view). For each breakpoint, we’ll store min-width, max-width, a base font-size, and a base line-height.

Complex Map Syntax

Here’s how we’d store that data in a map. One large map will contain four keys (breakpoint labels) whose values are maps of the variables we need to store and use. In a readable format like this, we’ve got a little more than 450 characters and 26 lines.

$breakpoint-map: (
  small: (
    min-width: null,
    max-width: 479px,
    base-font: 16px,
    vertical-rhythm: 1.3
  ),
  medium: (
    min-width: 480px,
    max-width: 959px,
    base-font: 18px,
    vertical-rhythm: 1.414
  ),
  large: (
    min-width: 960px,
    max-width: 1099px,
    base-font: 18px,
    vertical-rhythm: 1.5
  ),
  xlarge: (
    min-width: 1100px,
    max-width: null,
    base-font: 21px,
    vertical-rhythm: 1.618
  )
);

Nested List Syntax

The nested list for storing this same data is much shorter. We no longer have keys attached to our data however, so we have to rely on looping through it, or calling it with nth() functions. That said, it’s much shorter than the map: less than 180 characters and only 6 lines.

$breakpoint-list: (
  (small, null, 479px, 16px, 1.3),
  (medium, 480px, 959px, 18px, 1.414),
  (large, 960px, 1099px, 18px, 1.5),
  (xlarge, 1100px, null, 21px, 1.618)
);

Comparing Loops

When it comes to typing out our data structure, typing lists takes about a third of the time of typing a map. If we need to loop through those values, however, how does that compare?

Complex Map Loop

We can use the following code to loop through the top level items in this map:

@each $label, $map in $breakpoint-map {}

The two variables at the beginning of this line ($label and $map) are dynamically assigned as the loop iterates through the data in the map. Each top-level piece of data has two components: a key and a value. We’re assigning the key to $label and the value (which is a nested map) to $map. Inside this loop, we can use the variables $label and $map and they’ll automatically represent the key and value of the current entry.

That loop will iterate four times, once for each nested map. To get useful data out of the nested map, however, we’ll need to use the map-get() function. This function takes two parameters – the name of the map and the name of the desired key – and returns the value associated with that key. It’s the Sass equivalent to PHP’s $array['key'] and $object->key or JavaScript’s object.key syntax.

To iterate through all the sub-maps with @each and assign their values to useful variables with map-get(), we end up with a 6-line, 220-character loop.

@each $label, $map in $breakpoint-map {
  $min-width: map-get($map, min-width);
  $max-width: map-get($map, max-width);
  $base-font: map-get($map, base-font);
  $vertical-rhythm: map-get($map, vertical-rhythm);
}

Nested List Loop

Nested lists really make looping efficient. With maps, we had to assign a map to a dynamic loop variable and then use map-get() to assign all its values to variables, but with lists, we can quickly assign all values to variables.

Since each item in the top-level list has the same five values in the same order, we can instantly assign each of those to a dynamic variable to use inside our loop. With those variables, we don’t need to use map-get() to assign sub-values to usable variables. The loop we need for nested lists is only two lines and less than 100 characters.

@each $label, $min-width, $max-width, $base-font, $vertical-rhythm in $breakpoint-list {
}

Nested List Warnings

Nested lists are a major developer performance win: overall, you’ll probably type less than half as much as you’d type if you were using a map. However, there’s a reason maps were added to Sass: they provide a feature lists don’t: key-value mapping.

Missing Values

If you’re going to rely on nested lists, you must be absolutely certain you know how many items each list will hold and what order they’ll be in. Let’s look at what would happen in our example above if we left out an item in one of our lists:

$breakpoint-list: (
  (small, null, 479px, 16px, 1.3),
  (medium, 480px, 959px, 18px, 1.414),
  (large, 960px, 1099px, 18px, 1.5),
  (xlarge, 1100px, 21px, 1.618)
);

p {
  @each $label, $min-width, $max-width, $base-font, $vertical-rhythm in $breakpoint-list {
    @if $min-width {
      @include breakpoint( $min-width ) {
        font-size: $base-font;
        line-height: $vertical-rhythm;
      }
    } @else {
      font-size: $base-font;
      line-height: $vertical-rhythm;
    }
  }
}

If we try running that code, the last list will break. It will correctly assign ‘xlarge’ to $label and ‘1100px’ to $min-width, but then it will assign ’21px’ to $max-width and ‘1.618’ to $base-font, leaving $vertical-rhythm blank. As a result, we end up with an invalid font-size declaration and a missing line-height property in the last breakpoint. Additionally, Sass doesn’t report an error for this, so we’d have no idea if things worked out or not. If we had tried using the max-width for a media query, we would have ended up with the font-size value (just 21px) – that would be a pretty useless max-width, I think!

If we’d used maps instead, the map-get() function would’ve given us what we needed even if one of the values were missing. That’s our trade-off: what we gain in simplicity and speed with lists, we lose in specificity and error-proofing with maps.

Querying a Specific List

A related concern with using nested lists is querying for a specific list. Since maps have keys, you can quickly access any of the sub-maps with map-get():

$medium-map: map-get($maps, medium);

To get the data from the medium list in the nested lists, we need a more complex function:

@function get-list($label) {
  @each $list in $breakpoint-list {
    @if nth($list, 1) == $label {
      @return $list;
    }
  }
  @return null;
}
$medium-list: get-list(medium);

That function loops through all the lists in $breakpoint-list, checks the first value for the label we want, and returns the list if it finds a match. If it gets to the end of the @each loop without finding a match, it’ll return null. It’s basically a quick homemade interpretation of map-get() for lists that use the first value as a faux key.

Missing Map Functions

Sass has quite a few useful functions for dealing with maps: those same functions don’t exist for nested lists. For example, you can use map-merge() to add additional key-value pairs to a map. Using map-merge() with shared keys will update the value for shared keys. You can add a new list using join() or append(), but faking the update feature of map-merge() would require another custom Sass function.

Another useful map function is map-has-key(). This function is useful for validating any custom function that relies on map-get(). There’s no comparable function for lists, however.

You can use SassyLists to mimic map functions with lists. (This library provided these functions before Sass added map support.)

Conclusion

Maps are more powerful than lists because of they use key-value pairs. The additional Sass map functions provide useful ways to find data and validate map values.

Nested Sass lists can be quicker to write and maintain, but they’re probably not as well-suited for error-checking or detailed querying as maps. Most of the time, I believe maps are the better option, in spite of the increased verbosity. For smaller chunks of code and single-use loops, I’ll occasionally use a nested list, but maps work better for project-wide settings and data storage.

Have you compared maps and nested lists in any of your work, or refactored code to prefer one over the other? Share your experience in the comments!

You can see the code used for this tutorial in this Sassmeister gist.

Frequently Asked Questions (FAQs) on Sass Maps vs Nested Lists

What are the key differences between Sass Maps and Nested Lists?

Sass Maps and Nested Lists are both powerful tools in the Sass preprocessor, but they have distinct differences. Sass Maps are like associative arrays in other programming languages, where each value is associated with a unique key. This makes it easy to retrieve, update, and manipulate data. On the other hand, Nested Lists are a series of values, similar to an array in JavaScript. They are best used when you need to store and iterate over a collection of values, but they lack the direct access to specific values that Maps offer.

How do I use Sass Maps?

To use Sass Maps, you first need to define a map with a pair of parentheses, with each key-value pair separated by a colon. For example, $map: (key1: value1, key2: value2). You can then access the values in the map using the map-get function, like so: map-get($map, key1).

Can I nest Sass Maps like Nested Lists?

Yes, you can nest Sass Maps just like Nested Lists. This is particularly useful when you want to group related data together. To access a value in a nested map, you would use the map-get function twice: map-get(map-get($map, key1), key2).

How do I iterate over a Sass Map?

You can iterate over a Sass Map using the @each directive. The @each directive takes two variables: the key and the value. Here’s an example: @each $key, $value in $map { … }.

What are the advantages of using Sass Maps over Nested Lists?

Sass Maps offer several advantages over Nested Lists. They allow for direct access to specific values, making your code more efficient and easier to read. They also provide built-in functions for manipulating maps, such as adding, removing, and updating key-value pairs.

Can I convert a Nested List to a Sass Map?

Yes, you can convert a Nested List to a Sass Map using the map-from-list function. This function takes a list of pairs and returns a map where each pair is a key-value pair in the map.

How do I use Nested Lists in Sass?

To use Nested Lists in Sass, you first need to define a list with a pair of parentheses, with each value separated by a space or comma. For example, $list: (value1, value2, value3). You can then access the values in the list using the nth function, like so: nth($list, 1).

Can I use Sass Maps and Nested Lists together?

Yes, you can use Sass Maps and Nested Lists together. For example, you could use a map to store a collection of lists, or vice versa. This can be useful for organizing complex data structures.

Are there any limitations to using Sass Maps or Nested Lists?

While Sass Maps and Nested Lists are powerful tools, they do have some limitations. For example, Sass Maps cannot have duplicate keys, and the order of keys in a map is not guaranteed. Similarly, Nested Lists can become difficult to manage if they become too large or complex.

How do I choose between using a Sass Map or a Nested List?

The choice between using a Sass Map or a Nested List depends on your specific needs. If you need direct access to specific values and the ability to manipulate data, a Sass Map may be the best choice. If you simply need to store and iterate over a collection of values, a Nested List may be sufficient.

James SteinbachJames Steinbach
View Author

James is a Senior Front-End Developer with almost 10 years total experience in freelance, contract, and agency work. He has spoken at conferences, including local WordPress meet-ups and the online WP Summit. James's favorite parts of web development include creating meaningful animations, presenting unique responsive design solutions, and pushing Sass’s limits to write powerful modular CSS. You can find out more about James at jamessteinbach.com.

sasssass mapsStuR
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week