<commit_before>package DOM::Tiny::Collection;

use strict;
use warnings;
use Carp 'croak';
use Exporter 'import';
use List::Util;
use Scalar::Util 'blessed';

our $VERSION = '0.001';

our @EXPORT_OK = ('c');

sub c { __PACKAGE__->new(@_) }

sub new {
  my $class = shift;
  return bless [@_], ref $class || $class;
}

sub TO_JSON { [@{shift()}] }

sub compact {
  my $self = shift;
  return $self->new(grep { defined && (ref || length) } @$self);
}

sub each {
  my ($self, $cb) = @_;
  return @$self unless $cb;
  my $i = 1;
  $_->$cb($i++) for @$self;
  return $self;
}

sub first {
  my ($self, $cb) = (shift, shift);
  return $self->[0] unless $cb;
  return List::Util::first { $_ =~ $cb } @$self if ref $cb eq 'Regexp';
  return List::Util::first { $_->$cb(@_) } @$self;
}

sub flatten { $_[0]->new(_flatten(@{$_[0]})) }

sub grep {
  my ($self, $cb) = (shift, shift);
  return $self->new(grep { $_ =~ $cb } @$self) if ref $cb eq 'Regexp';
  return $self->new(grep { $_->$cb(@_) } @$self);
}

sub join {
  join $_[1] // '', map {"$_"} @{$_[0]};
}

sub last { shift->[-1] }

sub map {
  my ($self, $cb) = (shift, shift);
  return $self->new(map { $_->$cb(@_) } @$self);
}

sub reduce {
  my $self = shift;
  @_ = (@_, @$self);
  goto &List::Util::reduce;
}

sub reverse { $_[0]->new(reverse @{$_[0]}) }

sub shuffle { $_[0]->new(List::Util::shuffle @{$_[0]}) }

sub size { scalar @{$_[0]} }

sub slice {
  my $self = shift;
  return $self->new(@$self[@_]);
}

sub sort {
  my ($self, $cb) = @_;

  return $self->new(sort @$self) unless $cb;

  my $caller = caller;
  no strict 'refs';
  my @sorted = sort {
    local (*{"${caller}::a"}, *{"${caller}::b"}) = (\$a, \$b);
    $a->$cb($b);
  } @$self;
  return $self->new(@sorted);
}

sub tap {
  my ($self, $cb) = (shift, shift);
  $_->$cb(@_) for $self;
  return $self;
}

sub to_array { [@{shift()}] }

sub uniq {
  my ($self, $cb) = (shift, shift);
  my %seen;
  return $self->new(grep { !$seen{$_->$cb(@_)}++ } @$self) if $cb;
  return $self->new(grep { !$seen{$_}++ } @$self);
}

sub _flatten {
  map { _ref($_) ? _flatten(@$_) : $_ } @_;
}

sub _ref { ref $_[0] eq 'ARRAY' || blessed $_[0] && $_[0]->isa(__PACKAGE__) }

1;

=encoding utf8

=head1 NAME

DOM::Tiny::Collection - Collection

=head1 SYNOPSIS

  use DOM::Tiny::Collection;

  # Manipulate collection
  my $collection = DOM::Tiny::Collection->new(qw(just works));
  unshift @$collection, 'it';
  say $collection->join("\n");

  # Chain methods
  $collection->map(sub { ucfirst })->shuffle->each(sub {
    my ($word, $num) = @_;
    say "$num: $word";
  });

  # Use the alternative constructor
  use DOM::Tiny::Collection 'c';
  c(qw(a b c))->join('/')->url_escape->say;

=head1 DESCRIPTION

L<DOM::Tiny::Collection> is an array-based container for collections of
L<DOM::Tiny> nodes based on L<Mojo::Collection>.

  # Access array directly to manipulate collection
  my $collection = DOM::Tiny::Collection->new(1 .. 25);
  $collection->[23] += 100;
  say for @$collection;

=head1 FUNCTIONS

L<DOM::Tiny::Collection> implements the following functions, which can be imported
individually.<commit_msg>collections can contain more than just nodes<commit_after>package DOM::Tiny::Collection;

use strict;
use warnings;
use Carp 'croak';
use Exporter 'import';
use List::Util;
use Scalar::Util 'blessed';

our $VERSION = '0.001';

our @EXPORT_OK = ('c');

sub c { __PACKAGE__->new(@_) }

sub new {
  my $class = shift;
  return bless [@_], ref $class || $class;
}

sub TO_JSON { [@{shift()}] }

sub compact {
  my $self = shift;
  return $self->new(grep { defined && (ref || length) } @$self);
}

sub each {
  my ($self, $cb) = @_;
  return @$self unless $cb;
  my $i = 1;
  $_->$cb($i++) for @$self;
  return $self;
}

sub first {
  my ($self, $cb) = (shift, shift);
  return $self->[0] unless $cb;
  return List::Util::first { $_ =~ $cb } @$self if ref $cb eq 'Regexp';
  return List::Util::first { $_->$cb(@_) } @$self;
}

sub flatten { $_[0]->new(_flatten(@{$_[0]})) }

sub grep {
  my ($self, $cb) = (shift, shift);
  return $self->new(grep { $_ =~ $cb } @$self) if ref $cb eq 'Regexp';
  return $self->new(grep { $_->$cb(@_) } @$self);
}

sub join {
  join $_[1] // '', map {"$_"} @{$_[0]};
}

sub last { shift->[-1] }

sub map {
  my ($self, $cb) = (shift, shift);
  return $self->new(map { $_->$cb(@_) } @$self);
}

sub reduce {
  my $self = shift;
  @_ = (@_, @$self);
  goto &List::Util::reduce;
}

sub reverse { $_[0]->new(reverse @{$_[0]}) }

sub shuffle { $_[0]->new(List::Util::shuffle @{$_[0]}) }

sub size { scalar @{$_[0]} }

sub slice {
  my $self = shift;
  return $self->new(@$self[@_]);
}

sub sort {
  my ($self, $cb) = @_;

  return $self->new(sort @$self) unless $cb;

  my $caller = caller;
  no strict 'refs';
  my @sorted = sort {
    local (*{"${caller}::a"}, *{"${caller}::b"}) = (\$a, \$b);
    $a->$cb($b);
  } @$self;
  return $self->new(@sorted);
}

sub tap {
  my ($self, $cb) = (shift, shift);
  $_->$cb(@_) for $self;
  return $self;
}

sub to_array { [@{shift()}] }

sub uniq {
  my ($self, $cb) = (shift, shift);
  my %seen;
  return $self->new(grep { !$seen{$_->$cb(@_)}++ } @$self) if $cb;
  return $self->new(grep { !$seen{$_}++ } @$self);
}

sub _flatten {
  map { _ref($_) ? _flatten(@$_) : $_ } @_;
}

sub _ref { ref $_[0] eq 'ARRAY' || blessed $_[0] && $_[0]->isa(__PACKAGE__) }

1;

=encoding utf8

=head1 NAME

DOM::Tiny::Collection - Collection

=head1 SYNOPSIS

  use DOM::Tiny::Collection;

  # Manipulate collection
  my $collection = DOM::Tiny::Collection->new(qw(just works));
  unshift @$collection, 'it';
  say $collection->join("\n");

  # Chain methods
  $collection->map(sub { ucfirst })->shuffle->each(sub {
    my ($word, $num) = @_;
    say "$num: $word";
  });

  # Use the alternative constructor
  use DOM::Tiny::Collection 'c';
  c(qw(a b c))->join('/')->url_escape->say;

=head1 DESCRIPTION

L<DOM::Tiny::Collection> is an array-based container for collections of
L<DOM::Tiny> nodes or other items based on L<Mojo::Collection>.

  # Access array directly to manipulate collection
  my $collection = DOM::Tiny::Collection->new(1 .. 25);
  $collection->[23] += 100;
  say for @$collection;

=head1 FUNCTIONS

L<DOM::Tiny::Collection> implements the following functions, which can be imported
individually.
{'fraction_non_alphanumeric': 0.22947167409293442, 'fraction_numerical': 0.010502864417568428, 'mean_word_length': 3.7977099236641223, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 77, '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': '5688012', 'n_tokens_mistral': 2532, 'n_tokens_neox': 2475, 'n_words': 652}