Tiefes Holen/Setzen in Maps

Avatar of Kitty Giraudel
Kitty Giraudel am

Beim Arbeiten an komplexen Sass-Architekturen ist es nicht ungewöhnlich, Sass-Maps zur Verwaltung von Konfigurationen und Optionen zu verwenden. Von Zeit zu Zeit werden Sie Maps innerhalb von Maps (möglicherweise auf mehreren Ebenen) sehen, wie diese aus o-grid.

$o-grid-default-config: (
    columns: 12,
    gutter: 10px,
    min-width: 240px,
    max-width: 1330px,
    layouts: (
        S:  370px,  // ≥20px columns
        M:  610px,  // ≥40px columns
        L:  850px,  // ≥60px columns
        XL: 1090px  // ≥80px columns
    ),
    fluid: true,
    debug: false,
    fixed-layout: M,
    enhanced-experience: true
);

Das Problem bei solchen Maps ist, dass es nicht einfach ist, Werte aus dem verschachtelten Baum zu lesen und zu schreiben. Dies ist definitiv etwas, das Sie in Funktionen verstecken möchten, um es nicht jedes Mal manuell tun zu müssen.

Deep Get

Tatsächlich ist das Erstellen einer Funktion zum Abrufen tief verschachtelter Werte aus einer Map sehr einfach.

/// Map deep get
/// @author Kitty Giraudel
/// @access public
/// @param {Map} $map - Map
/// @param {Arglist} $keys - Key chain
/// @return {*} - Desired value
@function map-deep-get($map, $keys...) {
    @each $key in $keys {
        $map: map-get($map, $key);
    }
    @return $map;
}

Wenn wir beispielsweise den Wert abrufen möchten, der der M-Layout zugeordnet ist, aus unserer Konfigurationsmap, ist es so einfach wie

$m-breakpoint: map-deep-get($o-grid-default-config, "layouts", "M");
// 610px

Beachten Sie, dass Anführungszeichen um Zeichenketten optional sind. Wir fügen sie nur aus Gründen der Lesbarkeit hinzu.

Deep Set

Auf der anderen Seite kann das Erstellen einer Funktion zum Setzen eines tief verschachtelten Schlüssels sehr mühsam sein.

/// Deep set function to set a value in nested maps
/// @author Kitty Giraudel
/// @access public
/// @param {Map} $map - Map
/// @param {List} $keys -  Key chaine
/// @param {*} $value - Value to assign
/// @return {Map}
@function map-deep-set($map, $keys, $value) {
  $maps: ($map,);
  $result: null;
  
  // If the last key is a map already
  // Warn the user we will be overriding it with $value
  @if type-of(nth($keys, -1)) == "map" {
    @warn "The last key you specified is a map; it will be overrided with `#{$value}`.";
  }
  
  // If $keys is a single key
  // Just merge and return
  @if length($keys) == 1 {
    @return map-merge($map, ($keys: $value));
  }
  
  // Loop from the first to the second to last key from $keys
  // Store the associated map to this key in the $maps list
  // If the key doesn't exist, throw an error
  @for $i from 1 through length($keys) - 1 {
    $current-key: nth($keys, $i);
    $current-map: nth($maps, -1);
    $current-get: map-get($current-map, $current-key);
    @if $current-get == null {
      @error "Key `#{$key}` doesn't exist at current level in map.";
    }
    $maps: append($maps, $current-get);
  }
  
  // Loop from the last map to the first one
  // Merge it with the previous one
  @for $i from length($maps) through 1 {
    $current-map: nth($maps, $i);
    $current-key: nth($keys, $i);
    $current-val: if($i == length($maps), $value, $result);
    $result: map-merge($current-map, ($current-key: $current-val));
  }
  
  // Return result
  @return $result;
}

Wenn wir nun den Wert aktualisieren möchten, der dem M-Layout zugeordnet ist, aus unserer Konfigurationsmap, können wir dies tun:

$o-grid-default-config: map-deep-set($o-grid-default-config, "layouts" "M", 650px);

Zusätzliche Ressourcen

Die obige Funktion ist nicht die einzige Lösung für dieses Problem.

Die Bibliothek Sassy-Maps bietet ebenfalls die Funktionen map-deep-set und map-deep-get. In ähnlicher Weise hat Kitty Giraudel auch eine jQuery-ähnliche extend-Funktion geschrieben, um das integrierte map-merge rekursiv zu machen und mehr als zwei Maps auf einmal zusammenzuführen.