Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request: Deep Merge #9

Open
Undistraction opened this issue Sep 10, 2014 · 10 comments
Open

Request: Deep Merge #9

Undistraction opened this issue Sep 10, 2014 · 10 comments

Comments

@Undistraction
Copy link

There is not going to be a recursive/deep-merge added to Sass core anytime soon, so I would like to suggest you add it to Sassy-maps.

Example use: I keep all my colour-refs inside a nested map structure. I would like to merge project-specific colour-values into defaults, overriding anything that is already present.

@Snugug
Copy link
Member

Snugug commented Sep 10, 2014

I'd recommend using Toolkit's Settings functionality to do what you're requesting. Otherwise, map-set-deep exists and should allow for that.

@Snugug Snugug closed this as completed Sep 10, 2014
@Undistraction
Copy link
Author

@Snugug I think it would be a really useful bit of functionality in Sassy Maps and would nicely complement map-get-deep and map-set-deep. In case you change your mind, here is a function that deep merges two maps:

@function map-merge-deep($map-old, $map-new, $overwrite: true){
  // Iterate through each value of the new map
  @each $key, $new-value in $map-new {
    // Check if that value already exists on the old map
    @if map-has-key($map-old, $key) {
      // There is an existing key
      $old-value: map-get($map-old, $key);
      @if type-of($new-value) == map and type-of($old-value) == map {
        // If both are maps, recurse regardless of $overwrite
        $merged-value: map-merge-deep($old-value, $new-value);
        $map-old: map-set($map-old, $key, $merged-value);
      }@else{
        // Otherwise check $overwrite
        @if $overwrite{
          $map-old: map-set($map-old, $key, $new-value);
        }
      }
    }@else{
      // There is no existing key so add
      $map-old: map-set($map-old, $key, $new-value);  
    }
  }
  @return $map-old
}

Sassmeister Gist here

@Snugug
Copy link
Member

Snugug commented Sep 11, 2014

I guess I'm not sure what you're looking for exactly. What's the difference in output between set-deep and merge-deep? Also, if you'd like code to be considered, as a PR please.

@Undistraction
Copy link
Author

I'm looking for a way to take two maps and combine them into a new map that contains all the values from the first map and all all the values from the second, overwriting the values from the first if they are already present.

The problem with map-merge is that it won't recurse into values that are themselves maps. The problem with map-set-deep is that I need to know the path into the map to change the value; to change all values I need to recurse through the map and call map-set-deep for each value to avoid blowing away existing values. The function above recurses through the map as deep as is needed and either creates or replaces values, ultimately returning the merged map.

If you look at the Sassmeister you will see the output is a merging of map-1 and map-2.

I would be more than happy to submit a pull request if this functionality is something you would find useful.

Here are two maps and the resulting merged map

$map-1:(
  alpha: 1,
  beta: 1,
  charlie:(
    delta: 1,
    echo: 1,
    foxtrot:(
      gamma: 1,
      hotel: 1
    )
  )
);

$map-2:(
  alpha: 2,
  charlie: (
    echo: 2,
    foxtrot: (
      hotel: 2,
      yankee:(
        omega: 2
      )
    )
  ),
  indigo: (
    juliette: 2
  )
);

$result:(
  alpha: 2, 
  beta: 1,
  charlie: (
    delta: 1,
    echo: 2,
    foxtrot: (
      gamma: 1,
      hotel: 2,
      yankee: (
        omega: 2
      ), 
      indigo: (
        juliette: 2
      )
    )
  )

@cibulka
Copy link

cibulka commented Oct 8, 2014

+1!!

@Undistraction
Copy link
Author

@cibulka There is a PR open with the function or you can grab it here

@cibulka
Copy link

cibulka commented Oct 8, 2014

Thanks for an awesomely quick response! And about my question I've just deleted - everything works, I've just, well, forgot to include sassy-maps to my configuration. :) Thanks again, this helps me a lot!

@Undistraction
Copy link
Author

@cibulka No problem. Glad it's helpful.

@Undistraction
Copy link
Author

Gentle bump.

@markmartirosian
Copy link

The following works for me

@function spire-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: spire-map-extend(map-get($map, $key), $value, true);
        }

        // Merge current tuple with map
        $map: map-merge($map, ($key: $value));
      }
    }
  }

  @return $map;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants