@function __group-by-aggregator($result, $value, $key, $args...) {
  @if map-has-key($result, $key) {
    $result: __set($result, $key, append(__get($result, $key), $value));
  } @else {
    $aggregate: append((), $value);

    $result: map-merge(
      $result,
      (
        $key: $aggregate
      )
    );
  }

  @return $result;
}

@function __group-by($arguments...) {
  $function: __create-aggregator("__group-by-aggregator");

  @return __exec($function, $arguments...);
}

@function __group-by-keys($list, $args...) {
  $result: ();

  @each $item in $list {
    $item: __to-map($item);

    @each $key, $value in $item {
      @if map-has-key($result, $key) {
        $result: __set($result, $key, append(__get($result, $key), $value));
      } @else {
        $result: __set($result, $key, ($value));
      }
    }
  }

  @return $result;
}

/// Creates a map composed of keys generated from the results of running
/// each element of `$collection` through `$iteratee`. The corresponding value
/// of each key is a list of the elements responsible for generating the key.
/// The `$iteratee` is bound to `$this-arg` and invoked with three arguments;
/// (value, index|key, collection).
///
/// If a property name is provided for `$predicate` the created `_property`
/// style callback returns the property value of the given element.
///
/// If a value is also provided for `$this-arg` the created `_matches-property`
/// style callback returns `true` for elements that have a matching property
/// value, else `false`.
///
/// If a map is provided for `$predicate` the created `_matches` style
/// callback returns `true` for elements that have the properties of the given
/// object, else `false`.
///
///
/// @access public
/// @group Collection
/// @param {List|Map|string} $collection The collection to iterate over.
/// @param {Function|Map|string} $iteratee [_identity] - The function invoked
///  per iteration.
/// @param {*} $this-arg [null] - The `_this` binding of `$iteratee`.
/// @returns {Map} Returns the composed aggregate object.
/// @example scss
/// $foo: _group-by((4.2, 6.1, 6.4), floor);
/// // => { '4': (4.2,), '6': (6.1, 6.4) }

@function _group-by($args...) {
  @return call(get-function("__group-by"), $args...);
}

/// Creates a map composed of keys generated from the results of aggregating
/// the values for each key in `$collection` into a `list`.
///
///
/// @access public
/// @group Collection
/// @param {List|Map|string} $collection The collection to iterate over.
/// @returns {Map} Returns the aggregate values from the keys of the items.
/// @example scss
/// $foo: (a: 1, b: 2);
/// $bar: (a: 3, b: 4, c: 5);
///
/// $result: _group-by-keys($foo $bar);
/// // => (a: (1, 3), b: (2, 4), c: (5,));
@function _group-by-keys($args...) {
  @return call(get-function("__group-by-keys"), $args...);
}
