Deep Get in Map
/// An equivalent of `zip` function but for maps.
/// Takes two lists, the first for keys, second for values.
/// @param {List} $keys - Keys for map
/// @param {List} $values - Values for map
/// @return {Map} Freshly created map
@function map-zip($keys, $values) {
$l-keys: length($keys);
$l-values: length($values);
$min: min($l-keys, $l-values);
$map: ();
@if $l-keys != $l-values {
@warn "There are #{$l-keys} key(s) for #{$l-values} value(s) in the map for `map-zip`. "
+ "Resulting map will only have #{$min} pairs.";
}
@if $min == 0 {
@return $map;
}
@for $i from 1 through $min {
$map: map-merge($map, (nth($keys, $i): nth($values, $i)));
}
@return $map;
}
// Test if map got all `$keys` nested with each others
// @param {Map} $map - Map
// @param {Arglist} $keys - Keys to test
// @return {Bool}
@function map-has-nested-keys($map, $keys...) {
@each $key in $keys {
@if not map-has-key($map, $key) {
@return false;
}
$map: map-get($map, $key);
}
@return true;
}
// Test if map got all `$keys` at first level
// @param {Map} $map - Map
// @param {Arglist} $keys - Keys to test
// @return {Bool}
@function map-has-keys($map, $keys...) {
@each $key in $keys {
@if not map-has-key($map, $key) {
@return false;
}
}
@return true;
}
/// jQuery-style extend function
/// About `map-merge()`:
/// * only takes 2 arguments
/// * is not recursive
/// @param {Map} $map - first map
/// @param {ArgList} $maps - other maps
/// @param {Bool} $deep - recursive mode
/// @return {Map}
@function map-extend($map, $maps.../*, $deep */) {
$last: nth($maps, -1);
$deep: $last == true;
$max: if($deep, length($maps) - 1, length($maps));
// Loop through all maps in $maps...
@for $i from 1 through $max {
// Store current map
$current: nth($maps, $i);
// If not in deep mode, simply merge current map with map
@if not $deep {
$map: map-merge($map, $current);
} @else {
// If in deep mode, loop through all tuples in current map
@each $key, $value in $current {
// If value is a nested map and same key from map is a nested map as well
@if type-of($value) == "map" and type-of(map-get($map, $key)) == "map" {
// Recursive extend
$value: map-extend(map-get($map, $key), $value, true);
}
// Merge current tuple with map
$map: map-merge($map, ($key: $value));
}
}
}
@return $map;
}
// Update a key deeply nested
// @param {Map} $map - Map to update
// @param {Arglist} $keys - Keys to access to value to update
// @param {*} $value - New value (last member of `$keys`)
// @return {Map} - Updated map
@function map-deep-set($map, $keys.../*, $value */) {
$map-list: ($map,);
$result: null;
@if length($keys) == 2 {
@return map-merge($map, (nth($keys, 1): nth($keys, -1)));
}
@for $i from 1 through length($keys) - 2 {
$map-list: append($map-list, map-get(nth($map-list, -1), nth($keys, $i)));
}
@for $i from length($map-list) through 1 {
$result: map-merge(nth($map-list, $i), (nth($keys, $i): if($i == length($map-list), nth($keys, -1), $result)));
}
@return $result;
}
// Fetch nested keys
// @param {Map} $map - Map
// @param {Arglist} $keys - Keys to fetch
// @return {*}
@function map-deep-get($map, $keys...) {
@each $key in $keys {
$map: map-get($map, $key);
}
@return $map;
}
// Compute the maximum depth of a map
// @param {Map} $map
// @return {Number} max depth of `$map`
@function map-depth($map) {
$level: 1;
@each $key, $value in $map {
@if type-of($value) == "map" {
$level: max(map-depth($value) + 1, $level);
}
}
@return $level;
}