@function __base-map-iteratee($value, $key, $collection) {
    $result: __this('result');
    $iteratee: __this('iteratee');

    $result: append($result, __exec($iteratee, $value, $key, $collection));
    $_: __this('result', $result);

    @return $result;
}


@function __base-map($collection, $iteratee) {
    $_: __scope((
        'result': (),
        'iteratee': $iteratee
    ));
    $_: __base-each($collection, '__base-map-iteratee');
    $result: __this('result');
    $_: __scope(false);

    @return $result;
}


@function __list-map($list, $iteratee) {
    $index: 1;
    $length: length($list);
    $result-list: ();

    @while ($index <= $length) {
        $value: nth($list, $index);
        $result-list: append($result-list, __exec($iteratee, $value, $index, $list));
        $index: $index + 1;
    }

    @return $result-list;
}


@function __map($collection, $iteratee: '__identity', $this-arg: null) {
    @if not __is-iterable($collection) {
        $collection: ();
    }

    $collection: if(__is-string($collection), __to-list($collection), $collection);
    $function: if(__is-list($collection), '__list-map', '__base-map');
    $iteratee: __get-callback($iteratee, $this-arg, 3);

    @return __exec($function, $collection, $iteratee);
}


@function __map-values-iteratee($value, $key, $map) {
    $result: __this('result');
    $iteratee: __this('iteratee');
    $result: __set($result, $key, __exec($iteratee, $value, $key, $map));
    $_: __this('result', $result);

    @return true;
}


@function __map-values($map, $iteratee: __identity, $this-arg: null) {
    $result: ();
    $iteratee: __get-callback($iteratee, $this-arg, 3);
    $_: __scope((
        'result': $result,
        'iteratee': $iteratee
    ));
    $map-values-iteratee: __bind('__map-values-iteratee');
    $_: __base-for-own($map, $map-values-iteratee);
    $result: __this('result');
    $_: __scope(false);

    @return $result;
}


@function __map-keys-iteratee($value, $key, $map) {
    $result: __this('result');
    $iteratee: __this('iteratee');
    $iteratee-value: if(__this('with-keys'),
        $key,
        $value);

    $result: __set($result,
        __exec($iteratee, $iteratee-value, $key, $map),
        $value);
    $_: __this('result', $result);

    @return $result;
}


@function __map-keys($map, $iteratee: __identity, $this-arg: null, $with-keys: false) {
    $result: ();
    $iteratee: __get-callback($iteratee, $this-arg, 3);
    $_: __scope((
        'result': $result,
        'iteratee': $iteratee,
        'with-keys': $with-keys
    ));
    $map-keys-iteratee: __bind('__map-keys-iteratee');

    $_: __base-for-own($map, $map-keys-iteratee);
    $result: __this('result');
    $_: __scope(false);

    @return $result;
}


/// Creates a list of values by running each element in `$collection` through
/// `$iteratee`. 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`.
/// 
/// Many lodash methods are guarded to work as interatees for methods like
/// `_every`, `_filter`, `_map`, `_map-values`, `_reject`, and `_some`.
///
/// @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.
///  create a `_property` or `_matches` style callback respectively.
/// @param {*} $this-arg [null] - The `_this` binding of `$iteratee`.
/// @returns {List} Returns the new mapped list.
/// @example scss
///     @function times-three($n, $args...) {
///       @return $n * 3;
///     }
///     
///     $foo: _map((1, 2), times-three);
///     // => (3, 6)
///     
///     $foo: _map(( 'a': 1, 'b': 2 ), times-three);
///     // => (3, 6)
///     
///     $users: (
///       ( 'user': 'barney' ),
///       ( 'user': 'fred' )
///     );
///     // using the `_property` callback shorthand
///     $foo: _map($users, 'user');
///     // => ('barney', 'fred')

@function _map($args...) {
    @return call(get-function('__map'), $args...);
}


/// @alias _map

@function _collect($args...) {
    @return call(get-function('__map'), $args...);
}


/// Creates a map with the same keys as `$map` and values generated by
/// running each own enumerable property of `$map` through `$iteratee`. The
/// iteratee function is bound to `$this-arg` and invoked with three arguments;
/// (value, key, map).
/// 
/// If a property name is provided for `$iteratee` 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 `$iteratee` the created `_matches` style
/// callback returns `true` for elements that have the properties of the given
/// map, else `false`.
///
///
/// @access public
/// @group Map
/// @param {Map} $map The map 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 new mapped map.
/// @example scss
///     @function times-three($n, $args...) {
///         @return $n * 3;
///     }
///     
///     $foo: _map-values(( 'a': 1, 'b': 2 ), times-three);
///     // => ( 'a': 3, 'b': 6 )
///     
///     $users: (
///       'fred':    ( 'user': 'fred',    'age': 40 ),
///       'pebbles': ( 'user': 'pebbles', 'age': 1 )
///     );
///     // using the `_property` callback shorthand
///     $foo: _map-values($users, 'age');
///     // => ( 'fred': 40, 'pebbles': 1 )

@function _map-values($args...) {
    @return call(get-function('__map-values'), $args...);
}


/// The opposite of `_map-values`; this method creates a map with the same 
/// values as `$map` and keys generated by running each own enumerable 
/// property of `$map` through `$iteratee`.
/// 
/// If a property name is provided for `$iteratee` 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 `$iteratee` the created `_matches` style
/// callback returns `true` for elements that have the properties of the given
/// map, else `false`.
/// 
/// **Also** - if `$with-keys` is specified as `true`, the iteratee
/// will only use each `key` of the `$map` as its value.
/// 
/// @access public
/// @group Map
/// @param {Map} $map The map 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 new mapped map.
/// @example scss
///     @function to-css-prop($val, $key, $args...) {
///         @return _kebab-case($key);
///     }
///     
///     $foo: (
///         'backgroundColor': white,
///         'marginTop': 1rem
///     );
///     
///     $foo: _map-keys($foo, to-css-prop);
///     // => (
///     //  'background-color': white,
///     //  'margin-top': 1rem
///     // )
///     
///     // Using $with-keys
///     $foo: _map-keys($foo, _kebab-case, null, $with-key: true);
///     // => (
///     //  'background-color': white,
///     //  'margin-top': 1rem
///     // )

@function _map-keys($args...) {
    @return call(get-function('__map-keys'), $args...);
}