=begin pod :kind("Type") :subkind("class") :category("composite")

=TITLE class Map

=SUBTITLE Immutable mapping from strings to values

    class Map does Associative does Iterable { }

A C<Map> is an immutable mapping from string keys to values of arbitrary
types. It serves as a base class for L<Hash|/type/Hash>, which is mutable.

In list context a C<Map> behaves as a list of L<Pair|/type/Pair> objects.

Note that the order in which keys, values and pairs are retrieved is
generally arbitrary, but the C<keys>, C<values> and C<pairs> methods
return them always in the same order when called on the same object.

    my %e := Map.new('a', 1, 'b', 2);
    say %e.keys;    # can print «a b␤» or «b a␤»
    say %e.values;  # prints «1 2␤» if the previous line
                    # printed «a b␤», «2 1␤» otherwise


To retrieve a value from the Map by key, use the C<{ }> postcircumfix
operator:

    my %map is Map = 'a', 1, 'b', 2;
    say %map{'a'};      # OUTPUT: «1␤»
    say %map{ 'a', 'b' }; # OUTPUT: «(1 2)␤»

To check whether a given key is stored in a Map, modify the access
with the C<:exists> adverb:

    my $map = Map.new('a', 1, 'b', 2);
    my $key = 'a';
    if $map{$key}:exists {
        say "$map{} has key $key";
    }

Being an immutable instance, it is not possible to add keys after
a C<Map> has been initialized:

   my $m = Map.new( 'a', 1, 'b', 2 );
   $m{ 'c' } = 'foo'; # WRONG!
                      # Cannot modify an immutable Str

Unlike its mutable companion type L<Hash>, Map cannot be parameterized by key or value types.

=head1 Methods

=head2 method new

Defined as:

    method new(*@args)

Creates a new Map from a list of alternating keys and values, with
L<the same semantics|/language/hashmap#Hash_assignment> as described
in the L<Hashes and maps|/language/hashmap> documentation, but also
accepts C<Pair>s instead of separate keys and values. Use the
L<grouping operator|/language/operators#term_(_)> or quote the key to
ensure that a literal pair is not interpreted as a named argument.

    my %h = Map.new('a', 1, 'b', 2);

    # WRONG: :b(2) interpreted as named argument
    say Map.new('a', 1, :b(2)).keys; # OUTPUT: «(a)␤»

    # RIGHT: :b(2) interpreted as Pair because of extra parentheses
    say Map.new( ('a', 1, :b(2)) ).keys.sort; # OUTPUT: «(a b)␤»

    # RIGHT: 'b' => 2 always creates a Pair
    say Map.new('a', 1, 'b' => 2).keys.sort; # OUTPUT: «(a b)␤»

A shorthand syntax for creating Maps is provided:

    my %h is Map = 'a', 1, 'b', 2;

=head2 method elems

Defined as:

    method elems(Map:D: --> Int:D)

Returns the number of pairs stored in the Map.

    my %map = Map.new('a', 1, 'b', 2);
    say %map.elems; # OUTPUT: «2␤»

=head2 method ACCEPTS

Defined as:

    multi method ACCEPTS(Map:D: Positional $topic)
    multi method ACCEPTS(Map:D: Cool:D     $topic)
    multi method ACCEPTS(Map:D: Regex      $topic)
    multi method ACCEPTS(Map:D: Any        $topic)

Used in smartmatching if the right-hand side is an C<Map>.

If the topic is list-like (L<Positional|/type/Positional>), returns True if
any of the list elements exist as a key in the Map.

If the topic is of type C<Cool> (strings, integers etc.),
returns True if the topic exists as a key.

If the topic is a regex, returns True if any of the keys match
the regex.

As a fallback, the topic is coerced to a list, and the C<Positional>
behavior is applied.


=head2 method gist

Defined as:

    method gist(Map:D: --> Str:D)

Returns the string containing the "gist" of the L<Map|/type/Map>,
sorts the pairs and lists B<up to the first 100>,
appending an ellipsis if the L<Map|/type/Map> has more than 100 pairs.

=head2 method keys

Defined as:

    method keys(Map:D: --> Seq:D)

Returns a C<Seq> of all keys in the Map.

    my $m = Map.new('a' => (2, 3), 'b' => 17);
    say $m.keys; # OUTPUT: «(a b)␤»

=head2 method values

Defined as:

    method values(Map:D: --> Seq:D)

Returns a C<Seq> of all values in the Map.

    my $m = Map.new('a' => (2, 3), 'b' => 17);
    say $m.values; # OUTPUT: «((2 3) 17)␤»

=head2 method pairs

Defined as:

    method pairs(Map:D: --> Seq:D)

Returns a C<Seq> of all pairs in the Map.

    my $m = Map.new('a' => (2, 3), 'b' => 17);
    say $m.pairs; # OUTPUT: «(a => (2 3) b => 17)␤»

=head2 method antipairs

Defined as:

    method antipairs(Map:D: --> Seq:D)

Returns all keys and their respective values as a L<Seq|/type/Seq> of C<Pair>s
where the keys and values have been exchanged, i.e. the opposite of method
L<pairs|/type/Map#method_pairs>. Unlike the
L<C<invert>|/type/Map#method_invert> method, there is no attempt to expand list
values into multiple pairs.

    my $m = Map.new('a' => (2, 3), 'b' => 17);
    say $m.antipairs;                        # OUTPUT: «((2 3) => a 17 => b)␤»

=head2 method invert

Defined as:

    method invert(Map:D: --> Seq:D)

Returns all keys and their respective values as a L<Seq|/type/Seq> of C<Pair>s
where the keys and values have been exchanged. The difference between C<invert>
and L<C<antipairs>|/type/Map#method_antipairs> is that C<invert> expands list
values into multiple pairs.

    my $m = Map.new('a' => (2, 3), 'b' => 17);
    say $m.invert;                          # OUTPUT: «(2 => a 3 => a 17 => b)␤»

=head2 method kv

Defined as:

    method kv(Map:D: --> Seq:D)

Returns a C<Seq> of keys and values interleaved.

    Map.new('a', 1, 'b', 2).kv  # (a 1 b 2)

=head2 method list

Defined as:

    method list(Map:D: --> List:D)

Returns a C<List> of L<Pair|/type/Pair> objects of all keys and values in the Map.

    my $m = Map.new('a' => (2, 3), 'b' => 17);
    say $m.list;                            # OUTPUT: «(b => 17 a => (2 3))␤»

=head2 method sort

Defined as:

    multi method sort(Map:D: --> Seq:D)

Returns a L<Seq|/type/Seq> of L<Pair|/type/Pair> objects, which are the pairs of
the hash, sorted by key. Equivalent to C<%hash.sort: *.key>

    # These are equivalent:
    say Map.new(<c 3 a 1 b 2>).sort;        # OUTPUT: «(a => 1 b => 2 c => 3)␤»
    say Map.new(<c 3 a 1 b 2>).sort: *.key; # OUTPUT: «(a => 1 b => 2 c => 3)␤»

See L<Any.sort|/type/Any#method_sort> for additional available candidates.

=head2 method Int

Defined as:

    method Int(Map:D: --> Int:D)

Returns the number of pairs stored in the C<Map> (same as C<.elems>).

    my $m = Map.new('a' => 2, 'b' => 17);
    say $m.Int;                                       # OUTPUT: «2␤»

=head2 method Numeric

Defined as:

    method Numeric(Map:D: --> Int:D)

Returns the number of pairs stored in the C<Map> (same as C<.elems>).

    my $m = Map.new('a' => 2, 'b' => 17);
    say $m.Numeric;                                   # OUTPUT: «2␤»

=head2 method Bool

Defined as:

    method Bool(Map:D: --> Bool:D)

Returns C<True> if the invocant contains at least one key/value pair.

    my $m = Map.new('a' => 2, 'b' => 17);
    say $m.Bool;                                      # OUTPUT: «True␤»

=head2 method Capture

Defined as:

    method Capture(Map:D:)

Returns a L<Capture|/type/Capture> where each key, if any, has been converted
to a named argument with the same value as it had in the original C<Map>.
The returned C<Capture> will not contain any positional arguments.

    my $map = Map.new('a' => 2, 'b' => 17);
    my $capture = $map.Capture;
    my-sub(|$capture);                                # OUTPUT: «2, 17»

    sub my-sub(:$a, :$b) {
        say "$a, $b"
    }

=end pod

# vim: expandtab softtabstop=4 shiftwidth=4 ft=perl6
