Deep Get / Set en mapas | Programar Plus

Cuando se trabaja en arquitecturas Sass complejas, no es raro utilizar mapas Sass para mantener la configuración y las opciones. De vez en cuando, verá mapas dentro de mapas (posiblemente en varios niveles) como este de 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
);

El problema con estos mapas es que no es fácil obtener y establecer valores del árbol anidado. Definitivamente, esto es algo que desea ocultar dentro de las funciones para evitar tener que hacerlo manualmente cada vez.

Obtener profundo

En realidad, crear una función para obtener valores profundamente anidados de un mapa es muy fácil.

/// 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;
}

Por ejemplo, si queremos obtener el valor asociado a la M diseño de nuestro mapa de configuración, es tan fácil como:

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

Tenga en cuenta que las comillas alrededor de las cadenas son opcionales. Solo los agregamos por motivos de legibilidad.

Conjunto profundo

Por otro lado, crear una función para establecer una clave profundamente anidada puede resultar muy tedioso.

/// 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;
}

Ahora si queremos actualizar el valor asociado al M diseño de nuestro mapa de configuración, podemos hacer:

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

Recursos extra

La función anterior no es la única solución a este problema.

La biblioteca de Sassy-Maps también ofrece map-deep-set y map-deep-get funciones. En la misma linea, Gatito Giraudel también ha escrito un estilo jQuery extend función para hacer el incorporado map-merge recursivo y capaz de fusionar más de 2 mapas a la vez.

(Visited 4 times, 1 visits today)