<commit_before>=begin pod

=TITLE class Hash

=SUBTITLE Mapping from strings to itemized values

    class Hash is Map { }

A Hash is a mutable mapping from keys to values (called I<dictionary>,
I<hash table> or I<map> in other programming languages). The values are
all scalar containers, which means you can assign to them.

Hashes are usually stored in variables with the percent C<%> sigil.

Hash elements are accessed by key via the C<{ }> postcircumfix operator:

    my $home = %*ENV{'HOME'};

For literal string keys without whitespace, C< < > > can be used instead:

    my $home = %*ENV<HOME>;

You can add new pairs simply by assigning to an unused key:

    my %h;
    %h{'new key'} = 'new value';

=head1 Hash assignment

Assigning a list of elements to a hash variable first empties the variable,
and then iterates the elements of the right-hand side. If an element is an
L<Enum> (or a subclass like L<Pair>), its key is taken as a new hash key,
and its value as the new hash value for that key. Otherwise the value
is coerced to L<Str> and used as a hash key, while the next element of the
list is taken as the corresponding value.

    my %h = 'a', 'b', c => 'd', 'e', 'f';
    # same as
    my %h = a => 'b', c => 'd', e => 'f';
    # or
    my %h = <a b c d e f>;

If an L<Enum> is encountered where a value is expected, it is used as a
hash value:

    my %h = 'a', 'b' => 'c';
    say %h<a>.WHAT;             # Pair();
    say %h<a>.key;              # b

If the same key appears more than once, the value associated with its last
occurrence is stored in the hash:

    my %h = a => 1, a => 2;
    say %h<a>;                  # 2

To assign to a variable without the C<%> sigil, you may use curly braces:

    my $h = { a => 1, b => 2 };

Because C<{ }> is also used to denote blocks, you may prefix it with a colon
(X<:{ ... }|:{ }>) to force hash literal context:

    my $when = :{ (now) => "Instant", (DateTime.now) => "DateTime" };

=head1 Looping over hash keys and values

A common idiom for processing the elements in a hash is to loop over the
keys and values, for instance,

    my %vowels = 'a' => 1, 'e' => 2, 'i' => 3, 'o' => 4, 'u' => 5;
    for %vowels.kv -> $vowel, $index {
	    "$vowel: $index".say;
    }

gives output similar to this:

    a: 1
    e: 2
    o: 4
    u: 5
    i: 3

where we have used the C<kv> method to extract the keys and their respective
values from the hash, so that we can pass these values into the loop.

Note that the order of the keys and values printed cannot be relied upon;
the elements of a hash are not always stored the same way in memory for
different runs of the same program.  Sometimes one wishes to process the
elements sorted on, e.g. the keys of the hash.  If one wishes to print the
list of vowels in alphabetical order then one would write

    my %vowels = 'a' => 1, 'e' => 2, 'i' => 3, 'o' => 4, 'u' => 5;
    for %vowels.sort(*.key)>>.kv -> ($vowel, $index) {
	    "$vowel: $index".say;
    }

which prints

    a: 1
    e: 2
    i: 3
    o: 4
    u: 5

and is in alphabetical order as desired.  To achieve this result, we sorted
the hash of vowels by key (C<%vowels.sort(*.key)>) which we then ask for its
keys and values by applying the C<.kv> method to each element via the unary
C< >> > hyperoperator.  The output of this operation is a L<Seq>, the
items of which need to be passed into the C<for> loop's block as arguments,
which is why you will find the key/value pairs wrapped in parentheses. The signature
of the point block could also be a single argument.

  for %vowels.kv -> $vowel {
    say $vowel;
  }

This would print the list one line at a time.


=head2 Object hashes and type constraints

Hash keys are stored as L<Str> values in a normal hash. In many cases this is
sufficient, but sometimes you want the hash to preserve the objects you are using
as keys I<as the *exact* objects you are providing to the hash to use as keys>.
In these moments you want an object hash!

  my %intervals{Instant};
  my $first-instant = now;
  %intervals{ $first-instant } = "Our first milestone.";
  sleep 1;
  my $second-instant = now;
  %intervals{ $second-instant } = "Logging this Instant for spurious raisins.";
  for %intervals.kv.sort(* cmp *) -> (Instant $instant, $note) {
    state $last-instant //= $instant;
    say "We noted '$note' at $instant, with an interval of {$instant - $last-instant}";
    $last-instant = $instant;
  }

This example uses an object hash that only accepts keys of type L<Instant> to
implement a rudimentary, yet type-safe, logging mechanism. We utilize a named L<state>
variable for keeping track of the previous C<Instant> so that we can provide an interval.

The whole point of object hashes is to keep keys as objects-in-themselves.
Currently object hashes utilize the L<WHICH> method of an object, which returns a
unique identifier for every mutable object. This is the keystone upon which the object
identity operator (L<===>) rests. Order and containers really matter here as the order of
C<.keys> is undefined and one anonymous list is never L<===> to another.

  > ($first-instant, $second-instant) ~~ %intervals.keys
  False
  > ($first-instant, $second-instant) ~~ %intervals.keys.sort(* cmp *)
  True
  > ($first-instant, $second-instant) === %intervals.keys.sort(* cmp *)
  False
  > $first-instant === %intervals.keys.sort(* cmp *).index(0)
  True

Since C<Instant> defines its own comparison methods, in our example a sort according to
L<cmp> will always provide the earliest instant object as the first element in the L<List>
it returns.

If you would like to accept any object whatsoever in your hash, you can use L<Any>!

  my %h{Any};
  %h{(now)} = "This is an Instant";
  %h{(DateTime.now)} = "This is a DateTime, which is not an Instant";
  %h{"completely different"} = "Monty Python references are neither DateTimes nor Instants";

There is a more concise syntax which uses binding.

  my %h := :{ (now) => "Instant", (DateTime.now) => "DateTime" }

The binding is necessary because an object hash is about very solid, specific objects,
which is something that binding is great at keeping track of but about which assignment doesn't
concern itself much.

=head1 Methods

=head2 method push

Defined as:

    multi method push(Hash:D: *@new)

Usage:

    push HASH, VALUES
    HASH.push(VALUES)
    HASH.push: (VALUES)

Adds the C<@new> elements to the hash with the same semantics as hash
assignment, but with three exceptions:

=item The hash isn't emptied first, i.e. old pairs are not deleted.
<commit_msg>Document in-place editing of hash values<commit_after>=begin pod

=TITLE class Hash

=SUBTITLE Mapping from strings to itemized values

    class Hash is Map { }

A Hash is a mutable mapping from keys to values (called I<dictionary>,
I<hash table> or I<map> in other programming languages). The values are
all scalar containers, which means you can assign to them.

Hashes are usually stored in variables with the percent C<%> sigil.

Hash elements are accessed by key via the C<{ }> postcircumfix operator:

    my $home = %*ENV{'HOME'};

For literal string keys without whitespace, C< < > > can be used instead:

    my $home = %*ENV<HOME>;

You can add new pairs simply by assigning to an unused key:

    my %h;
    %h{'new key'} = 'new value';

=head1 Hash assignment

Assigning a list of elements to a hash variable first empties the variable,
and then iterates the elements of the right-hand side. If an element is an
L<Enum> (or a subclass like L<Pair>), its key is taken as a new hash key,
and its value as the new hash value for that key. Otherwise the value
is coerced to L<Str> and used as a hash key, while the next element of the
list is taken as the corresponding value.

    my %h = 'a', 'b', c => 'd', 'e', 'f';
    # same as
    my %h = a => 'b', c => 'd', e => 'f';
    # or
    my %h = <a b c d e f>;

If an L<Enum> is encountered where a value is expected, it is used as a
hash value:

    my %h = 'a', 'b' => 'c';
    say %h<a>.WHAT;             # Pair();
    say %h<a>.key;              # b

If the same key appears more than once, the value associated with its last
occurrence is stored in the hash:

    my %h = a => 1, a => 2;
    say %h<a>;                  # 2

To assign to a variable without the C<%> sigil, you may use curly braces:

    my $h = { a => 1, b => 2 };

Because C<{ }> is also used to denote blocks, you may prefix it with a colon
(X<:{ ... }|:{ }>) to force hash literal context:

    my $when = :{ (now) => "Instant", (DateTime.now) => "DateTime" };

=head1 Looping over hash keys and values

A common idiom for processing the elements in a hash is to loop over the
keys and values, for instance,

    my %vowels = 'a' => 1, 'e' => 2, 'i' => 3, 'o' => 4, 'u' => 5;
    for %vowels.kv -> $vowel, $index {
	    "$vowel: $index".say;
    }

gives output similar to this:

    a: 1
    e: 2
    o: 4
    u: 5
    i: 3

where we have used the C<kv> method to extract the keys and their respective
values from the hash, so that we can pass these values into the loop.

Note that the order of the keys and values printed cannot be relied upon;
the elements of a hash are not always stored the same way in memory for
different runs of the same program.  Sometimes one wishes to process the
elements sorted on, e.g. the keys of the hash.  If one wishes to print the
list of vowels in alphabetical order then one would write

    my %vowels = 'a' => 1, 'e' => 2, 'i' => 3, 'o' => 4, 'u' => 5;
    for %vowels.sort(*.key)>>.kv -> ($vowel, $index) {
	    "$vowel: $index".say;
    }

which prints

    a: 1
    e: 2
    i: 3
    o: 4
    u: 5

and is in alphabetical order as desired.  To achieve this result, we sorted
the hash of vowels by key (C<%vowels.sort(*.key)>) which we then ask for its
keys and values by applying the C<.kv> method to each element via the unary
C< >> > hyperoperator.  The output of this operation is a L<Seq>, the
items of which need to be passed into the C<for> loop's block as arguments,
which is why you will find the key/value pairs wrapped in parentheses. The signature
of the point block could also be a single argument.

  for %vowels.kv -> $vowel {
    say $vowel;
  }

This would print the list one line at a time.


=head2 Object hashes and type constraints

Hash keys are stored as L<Str> values in a normal hash. In many cases this is
sufficient, but sometimes you want the hash to preserve the objects you are using
as keys I<as the *exact* objects you are providing to the hash to use as keys>.
In these moments you want an object hash!

  my %intervals{Instant};
  my $first-instant = now;
  %intervals{ $first-instant } = "Our first milestone.";
  sleep 1;
  my $second-instant = now;
  %intervals{ $second-instant } = "Logging this Instant for spurious raisins.";
  for %intervals.kv.sort(* cmp *) -> (Instant $instant, $note) {
    state $last-instant //= $instant;
    say "We noted '$note' at $instant, with an interval of {$instant - $last-instant}";
    $last-instant = $instant;
  }

This example uses an object hash that only accepts keys of type L<Instant> to
implement a rudimentary, yet type-safe, logging mechanism. We utilize a named L<state>
variable for keeping track of the previous C<Instant> so that we can provide an interval.

The whole point of object hashes is to keep keys as objects-in-themselves.
Currently object hashes utilize the L<WHICH> method of an object, which returns a
unique identifier for every mutable object. This is the keystone upon which the object
identity operator (L<===>) rests. Order and containers really matter here as the order of
C<.keys> is undefined and one anonymous list is never L<===> to another.

  > ($first-instant, $second-instant) ~~ %intervals.keys
  False
  > ($first-instant, $second-instant) ~~ %intervals.keys.sort(* cmp *)
  True
  > ($first-instant, $second-instant) === %intervals.keys.sort(* cmp *)
  False
  > $first-instant === %intervals.keys.sort(* cmp *).index(0)
  True

Since C<Instant> defines its own comparison methods, in our example a sort according to
L<cmp> will always provide the earliest instant object as the first element in the L<List>
it returns.

If you would like to accept any object whatsoever in your hash, you can use L<Any>!

  my %h{Any};
  %h{(now)} = "This is an Instant";
  %h{(DateTime.now)} = "This is a DateTime, which is not an Instant";
  %h{"completely different"} = "Monty Python references are neither DateTimes nor Instants";

There is a more concise syntax which uses binding.

  my %h := :{ (now) => "Instant", (DateTime.now) => "DateTime" }

The binding is necessary because an object hash is about very solid, specific objects,
which is something that binding is great at keeping track of but about which assignment doesn't
concern itself much.

=head2 In place editing of values

There may be times when you would like to modify the values of a hash while iterating over them.

    > my %answers = illuminatus => 23, hitchhikers => 42
    hitchhikers => 42, illuminatus => 23
    > for %answers.values -> $v { $v += 10 }
    Cannot assign to a readonly variable or a value

This is traditionally accomplished by sending both the key and the value as follows.

    for %answers.kv -> $k,$v { %answers{$k} = $v + 10 }

However, it is possible to leverage the signature of the block in order to specify that
you would like read-write access to the values.

    for %answers.values -> $v is rw { $v += 10 }

It is not, however, possible to do in-place editing of hash keys, even in the case of object hashes.

=head1 Methods

=head2 method push

Defined as:

    multi method push(Hash:D: *@new)

Usage:

    push HASH, VALUES
    HASH.push(VALUES)
    HASH.push: (VALUES)

Adds the C<@new> elements to the hash with the same semantics as hash
assignment, but with three exceptions:

=item The hash isn't emptied first, i.e. old pairs are not deleted.

{'fraction_non_alphanumeric': 0.10028776978417266, 'fraction_numerical': 0.005683453237410072, 'mean_word_length': 3.322450248756219, 'pattern_counts': {'":': 0, '<': 81, '<?xml version=': 0, '>': 160, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1728992', 'n_tokens_mistral': 4468, 'n_tokens_neox': 4218, 'n_words': 2143}