<commit_before>unit class ArrayHash does Associative does Positional;

use v6;

use KnottyPair ();

# Until :EXPORT works ...
our sub infix:«=x>» ($key, $value) is export {
    KnottyPair.new(:$key, :$value);
}

our sub infix:«=X>» ($key, $value is rw) is export {
    my $pair = KnottyPair.new(:$key, value => Any);
    $pair.bind-value($value);
    $pair;
}

=NAME ArrayHash - a data structure that is both Array and Hash

=begin DESCRIPTION

You can think of this as a L<Hash> that always iterates in insertion order or you can think of this as an L<Array> of L<Pair>s with fast lookups on tha values. Both are correct. Though, this class uses L<KnottyPair> internally, rather than plain old Pairs.

=end DESCRIPTION

has %!hash;
has KnottyPair @!array handles <
    elems Bool Int end Numeric Str
    flat list lol flattens Capture Parcel Supply
    pick roll reduce combinations
>;

method new(*@a, *%h) {
    my $self = self.bless;
    $self.push: |@a, |%h;
    $self
}

method of() {
    self.Positional::of();
}

method AT-KEY(ArrayHash:D: $key) { 
    %!hash{$key} 
}

method AT-POS(ArrayHash:D: $pos) returns KnottyPair {
    @!array[$pos];
}

method ASSIGN-KEY(ArrayHash:D: $key, $value is copy) { 
    if %!hash{$key} :exists {
        %!hash{$key} = $value;
    }
    else {
        @!array.push: $key =X> $value;
        %!hash{$key} := $value;
    }

    %!hash{$key} =:= @!array.first(*.key eqv $key).value
        or die "internal representation mismatch";
}

method ASSIGN-POS(ArrayHash:D: $pos, KnottyPair:D $pair is copy) {
    if @!array[$pos] :exists {
        %!hash{ @!array[$pos].key } :delete;
    }

    %!hash{ $pair.key } := $pair.value;
    @!array[ $pos ] = $pair;
}

method BIND-KEY(ArrayHash:D: $key, $value is rw) is rw { 
    if %!hash{$key} :exists {
        %!hash{$key} := $value;
        my $pos = @!array.first-index(*.key eqv $key);
        @!array[$pos].bind-value($value);
    }
    else {
        %!hash{$key} := $value;
        @!array.push: $key =X> $value;
    }
}

method BIND-POS(ArrayHash:D: $pos, KnottyPair:D $pair is rw) {
    if @!array[$pos] :exists {
        %!hash{ @!array[$pos].key } :delete;
    }

    %!hash{ $pair } := $pair.value;
    @!array[ $pos ] := $pair;
}

method EXISTS-KEY(ArrayHash:D: $key) {
    %!hash{$key} :exists;
}

method EXISTS-POS(ArrayHash:D: $pos) {
    @!array[$pos] :exists;
}

method DELETE-KEY(ArrayHash:D: $key) {
    if %!hash{$key} :exists {
        my $pos = @!array.first-index(*.key eqv $key);
        @!array.splice($pos, 1);
    }

    %!hash{$key} :delete;
}

method DELETE-POS(ArrayHash:D: $pos) returns KnottyPair {
    my KnottyPair $pair;
    if $pair = @!array[ $pos ] :delete {<commit_msg>Adding a flag to allow multivalued-ness<commit_after>unit class ArrayHash does Associative does Positional;

use v6;

use KnottyPair ();

# Until :EXPORT works ...
our sub infix:«=x>» ($key, $value) is export {
    KnottyPair.new(:$key, :$value);
}

our sub infix:«=X>» ($key, $value is rw) is export {
    my $pair = KnottyPair.new(:$key, value => Any);
    $pair.bind-value($value);
    $pair;
}

=NAME ArrayHash - a data structure that is both Array and Hash

=begin DESCRIPTION

You can think of this as a L<Hash> that always iterates in insertion order or you can think of this as an L<Array> of L<Pair>s with fast lookups on tha values. Both are correct. Though, this class uses L<KnottyPair> internally, rather than plain old Pairs.

=end DESCRIPTION

has %!hash;
has KnottyPair @!array handles <
    elems Bool Int end Numeric Str
    flat list lol flattens Capture Parcel Supply
    pick roll reduce combinations
>;

has Bool $multivalued;

method new(*@a, *%h, Bool :$multivalued = False) {
    my $self = self.bless(:$multivalued);
    $self.push: |@a, |%h;
    $self
}

submethod BUILD(:$!multivalued) { self }

method of() {
    self.Positional::of();
}

method !clear-before($pos, $key) returns Bool {
    my @pos = @!array[0 .. $pos - 1].grep-index(*.key eqv $key);
    @!array[@pos] :delete;
}

method !found-after($pos, $key) returns Bool {
    @!array[$pos + 1 .. @!array.end].first-index(*.key eqv $key) >= 0
}

method AT-KEY(ArrayHash:D: $key) { 
    %!hash{$key} 
}

method AT-POS(ArrayHash:D: $pos) returns KnottyPair {
    @!array[$pos];
}

method ASSIGN-KEY(ArrayHash:D: $key, $value is copy) { 
    POST { %!hash{$key} =:= @!array.first(*.key eqv $key).value }

    if %!hash{$key} :exists {
        %!hash{$key} = $value;
    }
    else {
        @!array.push: $key =X> $value;
        %!hash{$key} := $value;
    }
}

method ASSIGN-POS(ArrayHash:D: $pos, KnottyPair:D $pair is copy) {
    PRE  { $!multivalued || @!array.grep(*.key eqv $pair.key).elems <= 1 }
    POST { $!multivalued || @!array.grep(*.key eqv $pair.key).elems <= 1 }
    POST { %!hash{$pair.key} =:= @!array.first(*.key eqv $pair.key).value }

    if @!array[$pos] :exists && @!array[$pos].defined {
        %!hash{ @!array[$pos].key } :delete;
    }

    %!hash{ $pair.key } := $pair.value;
    @!array[ $pos ] = $pair;
}

method BIND-KEY(ArrayHash:D: $key, $value is rw) is rw { 
    POST { %!hash{$key} =:= @!array.first(*.key eqv $key).value }

    if %!hash{$key} :exists {
        %!hash{$key} := $value;
        my $pos = @!array.first-index(*.key eqv $key);
        @!array[$pos].bind-value($value);
    }
    else {
        %!hash{$key} := $value;
        @!array.push: $key =X> $value;
    }
}

method BIND-POS(ArrayHash:D: $pos, KnottyPair:D $pair is rw) {
    PRE  { $!multivalued || @!array.grep(*.key eqv $pair.key).elems <= 1 }
    POST { $!multivalued || @!array.grep(*.key eqv $pair.key).elems <= 1 }
    POST { %!hash{$pair.key} =:= @!array.first(*.key eqv $pair.key).value }

    if @!array[$pos] :exists {
        %!hash{ @!array[$pos].key } :delete;
    }

    %!hash{ $pair } := $pair.value;
    @!array[ $pos ] := $pair;
}

method EXISTS-KEY(ArrayHash:D: $key) {
    %!hash{$key} :exists;
}

method EXISTS-POS(ArrayHash:D: $pos) {
    @!array[$pos] :exists;
}

method DELETE-KEY(ArrayHash:D: $key) {
    if %!hash{$key} :exists {
        my $pos = @!array.first-index(*.key eqv $key);
        @!array.splice($pos, 1);
    }

    %!hash{$key} :delete;
}

method DELETE-POS(ArrayHash:D: $pos) returns KnottyPair {
    my KnottyPair $pair;
    if $pair = @!array[ $pos ] :delete {
{'fraction_non_alphanumeric': 0.1871819338422392, 'fraction_numerical': 0.0019083969465648854, 'mean_word_length': 3.091737150292778, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 24, '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': '4579648', 'n_tokens_mistral': 2486, 'n_tokens_neox': 2416, 'n_words': 635}