=encoding utf8

=head1 NAME

Module::Generic::Array - An Array Manipulation Object Class

=head1 SYNOPSIS

    my $ar = Module::Generic::Array->new( [qw( Joe John Mary )] );
    printf( "There are %d people\n", $ar->length );
    # Adding one more
    $ar->push( "Jack" );

=head1 VERSION

    v1.0.1

=head1 DESCRIPTION

The purpose of this class/package is to provide an object-oriented approach at array manipulation

=head1 METHODS

=head2 new

Provided with an optional array reference or an array object such as L<Module::Generic::Array>, this will create a new object and return it.

=head2 as_array

This simply returns the current object, so it does nothing special. It is here, so that one can blindly call C<as_array> on any of the L<Module::Generic::Array>, L<Module::Generic::Boolean>, L<Module::Generic::Number> or L<Module::Generic::Scalar> object and get an L<Module::Generic::Array> object in return.

=head2 as_string

Returns the array as string, which is essentially the same as C<"@array">

It takes an optional boolean value that, if true, will sort the array before.

=head2 as_hash

Returns an hash reference with the keys being the array elements and the hash values their offset value.

It takes an optional hash of options:

=over 4

=item I<start_from>

If true, the offset values will start from the number provided and not 0, as it is the case by default.

=back

Example:

    my $a = Module::Generic::Array->new( [qw( Jack John Peter )] );
    my $h = $a->as_hash({ start_from => 1 });
    # $h now is:
    {
        Jack => 1,
        John => 2,
        Peter => 3,
    }

=head2 chomp

 For example, assuming a file whose lines were read onto an array, and each being terminated by C<\n>, the following will remove each line's new lines characters.

    my $lines = Module::Generic::Array->new( \@lines );
    $lines->chomp;
    # Now @lines does not contain any \n at the end

=head2 clone

Creates a clone of the current array object and returns it.

=head2 delete

Provided with an offset value and an optional length, and this will remove data from the array at the given offset and for the given length.

If no given length is provided, it removes all entries from the offset until the end.

It returns a list of elements removed in list context or a new array object of it in scalar context.

See also L</splice>

=head2 each

Provided with a code reference such as a reference to a subroutine, and this will execute the code passing it the array offset and the current value as the 2 arguements. The current value is also accessible with C<$_>

    $a->each(sub
    {
        print( "I got $_\n" );
        # could also write:
        # print( "I got $_[1] at offset $_[0]\n" );
    });

To exit the loop, return C<undef()>, for example:

    $a->each(sub
    {
        return if( $_ eq $not_this_one );
        print( "ok, this one\n" );
    });

=head2 empty

This is just an intuitive alias for L</reset>

=head2 exists

Provided with a value and this returns the number of match, as an L<Module::Generic::Number> object, if it is found in the array, or false otherwise.

=head2 even

Returns a new L<Module::Generic::Array> object of elements whose position in the array are an even number, starting from 0.

For example:

my $a = Module::Generic::Array->new( [qw( 1 2 3 4 5 6 7 8 9 10 )] );
my $even = $a->even;
say "@$even";

Produces: C<1 3 5 7 9>

See also L</odd>

=head2 first

Returns the first element of the array, if any.

If there are none and this method is called in an object context, to ensure chaining will work, it will return a L<Module::Generic::Null> object. Otherwise, it will return simply the first element of the array whatever that is.

For example:

    # Getting the last_name property from a Person object stored in an array
    $a->first->last_name;

Even if there is no such Person object, L</first> will return a L<odule::Generic::Null> object so the call to C<last_name> does not ends up in a perl exception.

But:

    my $person = $a->first;

will return whatever is in the first position of the array in non-object context, and in object context if the value is not a reference of any sort, a L<Module::Generic::Scalar> object representing that value. For example:

    my $a = Module::Generic::Array->new( [qw( Hello world )] );
    say $a->first->length; # 5
    # but
    say $a->first; # returns "Hello" as a regular string

=head2 for

Provided with a subroutine reference and this will call the subroutine reference, passing it the offset number and the corresponding array value.

    $ar->for(sub
    {
        my( $i, $val ) = @_;
        # do something
    })

C<$_> is made available and contains the value of C<$val>

It returns the object itself so this can be chained.

To exit the loop, return C<undef()>, for example:

    $a->for(sub
    {
        return if( $_ eq $not_this_one );
        print( "ok, this one\n" );
    });

If you wanted to affect the current offset position relative to the current counter value, you can return a scalar reference containing an integer. For example:

    my $a = Module::Generic::Array->new( [qw( Jack John Peter Peter Gabriel Raphael Emmanuel )] );

    $a->for(sub
    {
        my( $i, $name ) = @_;
        if( $name eq 'Peter' )
        {
            $a->splice( $i, 1 );
            return( \-1 );
        }
        # return true
        return( 1 );
    });
    say "@$a"; # Jack John Gabriel Raphael Emmanuel

And if you wanted to modify the counter value itself, you can change the value of the first argument provided. For example:

    $a->for(sub
    {
        my( $i, $name ) = @_;
        if( $name eq 'Peter' )
        {
            $a->splice( $i, 1 );
            $_[0]--;
        }
        # return true
        return( 1 );
    });
    say "@$a"; # Jack John Gabriel Raphael Emmanuel

This is useful if you want to modify the array and avoid getting unexpected results.

=head2 foreach

Provided with a subroutine reference and this will call the subroutine reference, passing it the value for each entry in the array.

    $ar->foreach(sub
    {
        my $val = shift( @_ );
        # do something
    })

It returns the object itself so this can be chained.

P.S. : Do not use L</splice> while using L</foreach>. Perl wanrs you against it in L<perlsync|https://metacpan.org/pod/perlsyn#Foreach-Loops>: "foreach will get very confused if you add or remove elements within the loop body, for example with L</splice>. So don't do that."

Instead use L</for>. See example provided there on how to do it.

C<$_> is made available and contains the value of C<$val>

To exit the loop, return C<undef()>, for example:

    $a->foreach(sub
    {
        return if( $_ eq $not_this_one );
        print( "ok, this one\n" );
    });

=head2 get

Provided an integer representing an offset and this returns the corresponding value in the array. Offsets start from 0. A blank value will be treated as 0.

    my $a = Module::Generic::Array->new( [qw( abc def ghi )] );
    $a->get( 1 ); # def
    $a->get( '' ); # abc
    $a->get( undef() ); # abc
    $a->get( -1 ); # ghi

In non-object context, will return the value as is, and in object context and if the value is not a reference of any sort, it returns a L<Module::Generic::Scalar> object representing that value. For example:

    my $a = Module::Generic::Array->new( [qw( Hello world )] );
    say $a->get(0)->length; # 5
    # but
    say $a->get(0); # returns "Hello" as a regular string

See also L</index>

=head2 grep

Provided with some data, and this will do a grep on the array.

If the data provided is a code reference or a reference to a subroutine, the code reference will be called for each array entry, and C<$_> will also be available for each entry.

If the data is a regular expression, each array entry is tested against it.

It returns a list of matches found in ilst context and a new L<Module::Generic::Array> in scalar context.

=head2 has

This is an alias for L</exists>

=head2 index

Provided with an index of an element in the array and this returns its corresponding value as is in non-object context, and in object context and if the value is not a reference of any sort, it returns a L<Module::Generic::Scalar> object.

It takes an integer and this ensures the value used is an integer by applying L<perlfunc/int>

    my $a = Module::Generic::Array->new( [qw( John Jack Peter )] );
    $a->index( 1 ); # returns Jack

And in object context:

    say $a->index( 1 )->substr( 0, 2 ); # returns "Ja"

If there is nothing at the given offset, possibly because the array is smaller, then this would return undef in non-object context, or in object context, a L<Module::Generic::Scalar> object representing C<undef()>

=head2 iterator

This returns a new iterator to cycle through all the array items using iterator's method, such as L<Module::Generic::Iterator/next> and L<Module::Generic::Iterator/prev>. Each iterator element is an L<Module::Generic::Iterator::Element> object

    my $i = $a->iterator;
    while( $i->has_next )
    {
        my $elem = $i->next;
        my $value = $elem->value;
        # Get the next element relative to our element
        printf( "Next value is: %s at offset %d\n", $elem->next, $elem->next->pos  );
    }

=head2 join

Provided with a string, or expression just as documented in L<perlfunc/"join"> and this will return a string as an L<<Module::Generic::Scalar> object.

=head2 keys

This works as documented in L<perlfunc/"keys"> and returns a list of offset values for each entry in the array.

=head2 last

Returns the last element of the array, whatever that is, in non-object context, and in object context and if the value is not a reference of any sort, it returns a L<Module::Generic::Scalar> object representing that value.
If there are none, instead it will return a L<Module::Generic::Null> to ensure chaining will still work.

For example:

    my $a = Module::Generic::Array->new( [qw( Hello world )] );
    say $a->last->length; # 5
    # but
    say $a->last; # returns "world" as a regular string

=head2 length

Returns the size of the array, starting from 1, as a L<Module::Generic::Number> object.

This is different from L</size> that returns value from 0 and -1 if the array is empty.

=head2 list

Reeturns the array as a list

    my $a = Module::Generic::Array->new( [qw( Joe John Mary )] );
    print( "@$a" ); # Joe John Mary
    my @people = $a->list; # @people now is ( "Joe", "John", "Mary" )

=head2 map

Provided with a reference to a subroutine and this will call the subroutine for each element of the array and return a list in list context or a new L<Module::Generic::Array> otherwise.

For each iteration of the array, C<$_> is made available.

    print( $a->map(sub{ $_->value })->join( "\n" ), "\n" );

=head2 merge

Provided with a list of L<Module::Generic::Array> object and this will merge them with our current one, i.e. the one used to call this L</merge> method.

For example:

    my $a = Module::Generic::Array->new( [qw( Jack John Peter )] );
    my $b = Module::Generic::Array->new( [qw( Gabriel Raphael Emmanuel )] );
    $a->merge( $b );
    print( $a->join( ' ' ), "\n" ); # Jack John Peter Gabriel Raphael Emmanuel

Alternatively, you could simply do:

    $a->push( $b->list );

=head2 odd

Returns a new L<Module::Generic::Array> object of elements whose position in the array are an odd number, starting from 0.

For example:

my $a = Module::Generic::Array->new( [qw( 1 2 3 4 5 6 7 8 9 10 )] );
my $odd = $a->odd;
say "@$odd";

Produces: C<2 4 6 8 10>

See also L</even>

=head2 offset

This takes 2 integer as arguments: an offset and optionally a length and it will return a L<Module::Generic::Array> object representing this segment from the current array. Offset starts at 0.

For example:

    my $a = $m->new_array( [qw( Jack John Peter Gabriel Raphael Emmanuel )] );
    print( $a->offset( 2, 3 )->join( ' ' ), "\n" ); # John Peter Gabriel

Note that the a arguments are converted into an integer, so if you provide a non integer value, it will be turned into one. In this regard, C<undef> would torn into zero.

    print( $a->offset( 2, undef )->join( ' ' ), "\n" ); # returns nothing, because length (undef) became 0

If you provide a negative length, it will start back by that much from the offset point up to the offset point.

    print( $a->offset( 2, -1 )->join( ' ' ), "\n" ); # John Peter

And if the length is negative, it will start back from the end of the array

    print( $a->offset( 2, -3 )->join( ' ' ), "\n" ); # Emmanuel Jack John Peter

The offset itself can be negative and it will position it starting from the end of the array

    print( $a->offset( -2, 3 )->join( ' ' ), "\n" ); # Raphael Emmanuel Jack

If only an offset position is provided and no length, it will return all elements from that position until the end of the array:

    print( $a->offset(3)->join( ' ' ), "\n" ); # Gabriel Raphael Emmanuel

See L</splice> especially if you want to remove segments of elements from the current array, such as:

    print( $a->splice( 1 ) ); # removes and returns John Peter Gabriel Raphael Emmanuel

See L</get> or L</index> if you want to return only one element at a certain offset position.

=head2 pop

In non-object context, it returns the last entry in the array, whatever that is. In object context and if the value is not a reference of any sort, this returns a L<Module::Generic::Scalar> object.

For example:

    my $a = Module::Generic::Array->new( [qw( Hello world )] );
    say $a->pop->length; # 5
    # but
    say $a->pop; # returns "world" as a regular string

=head2 pos

Provided with some value (references are ok too), and this will return the position (starting from 0) of it in the array, or undef if nothing was found.

    my $a = Module::Generic::Array->new( [qw( John Jack Peter )] );
    my $offset = $a->pos( 'Jack' ); # returns 1
    $a->pos( 'Bob' ); # Returns undef
    my $hash = { first_name => 'John', last_name => 'Doe' };
    $a->push( $hash );
    $a->pos( $hash ); # Returns 3

Note that it returns the position in the array of the first occurrence found. Maybe I should consider returning a list of all occurrences in list context?

=head2 push

Provided with some data and this adds it at the end of the array.

Note that pushing an L<Module::Generic::Array> object into another L<Module::Generic::Array> object does not actually expand it, because this would imply makin an assumption on what the user actually wants. Thus, you would need to make that clear yourself.

    my $a = Module::Generic::Array->new( [qw( John Jack Peter )] );
    my $b = Module::Generic::Array->new( [qw( Gabriel Raphael Emmanuel )] );
    $a->push( $b );
    # Now $a contains something like: John Jack Peter Module::Generic::Array=ARRAY(0x557d2c2983e8)

Instead you might want to do:

    $a->push( $b->list );

Or if you prefer:

    $a->merge( $b ); # $a now is: John Jack Peter Gabriel Raphael Emmanuel

which will merge array object C<$b> into C<$a>, by pushing all of C<$b>' elements at the end of C<$a>'s stack.

=head2 push_arrayref

Provided with an array reference, and this add all its entry at the end of the array.

    my $ar = Module::Generic::Array->new( [qw( John Joe Mary )]);
    $ar->push_arrayref( [qw( Jack Peter )] );
    print( $ar->join( "," ), "\n" );
    # Now prints: John, Joe, Mary, Jack, Peter

=head2 remove

This takes an L<Module::Generic::Array> object, or an array reference, or a regular array (i.e. a list) of element and will remove them from the current array object. It works on reference too.

    my $dummy = { class => 'Hello' };
    my $a = Module::Generic::Array->new( ['Jack', 'John', $dummy, 'Paul', 'Peter', 'Gabriel', 'Raphael', 'Emmanuel'] );
    $a->remove( $dummy, qw( John Paul Peter Emmanuel ) );
    say( "@$a" ); # Jack Gabriel Raphael

It returns the current object.

=head2 reset

This empty the array, just like L</"undef">

=head2 return

This method is used to send the calling array a return value. It works across multiple embedded loops. For example:

Telling the loop for array C<$a> to terminate while we are in loop for array C<$b>:

    $a->for(sub
    {
        my( $i, $n ) = @_;
        $pos = $n;
        $b->for(sub
        {
            my( $j, $v ) = @_;
            # Tell loop for array $a to stop
            $a->return( undef() ) if( $n == 7 && $v == 27 );
        });
        # some more stuff here....
    });

Telling the loop for array C<$a> to skip 1:

    my $a = Module::Generic::Array->new( [qw( Jack John Peter Paul Gabriel Raphael Emmanuel )] );
    $a->for(sub
    {
        my( $i, $n ) = @_;
        # tell it to skip Peter
        $a->return( +1 ) if( $n eq 'John' );
        return( 1 );
    });

It works by having the loop for the given array check for a return value assign to it at each iteration. Because it is just a check in an hash, it is very lightweight. When a return value is found, it is reset immediately.

=head2 return_reset

Reset the return value used in methods that implement loop functions.

=head2 reverse

Returns a the array in reverse order in list context or a new L<Module::Generic::Array> object of it in scalar context.

=head2 scalar

Returns the size of the array. It basically calls L</length>

=head2 set

Provided with an array, an array reference or an array-based object and this replaces all the data in the current object by the ones provided.

Note that if an array object is provided, it will copy the content of that object and not set the array object itself.

    $a->set( qw( John Jack Peter ) ); # Using an array of elements
    $a->set( [qw( John Jack Peter )] ); # Using an array reference of elements
    $a->set( $a2 ); # Using another array object, whatever its class may be

=head2 shift

Remove the first entry and returns it as is.
In non-object context, it removes the first entry in the array and returns it, whatever that is. In object context and if the value is not a reference of any sort, this returns a L<Module::Generic::Scalar> object.

For example:

    my $a = Module::Generic::Array->new( [qw( Hello world )] );
    say $a->shift->length; # 5
    # but
    say $a->shift; # returns "Hello" as a regular string

=head2 size

Returns the size of the array starting from 0, and -1 if the array is empty, as a L<Module::Generic::Number> object.

This is equivalent to the perl variable C<$#> as documented in L<perldata>. It returns the last index in the array.

This is different from L</length> that returns value from 1 and 0 if the array is empty.

=head2 sort

Sort the array and return the new array as a list in list context or a new L<Module::Generic::Array> object in scalar context.

=head2 splice

Takes the same arguments as the L<perlfunc/"splice"> function, but its return value is different.

If L</splice> is called to add new element, the array object is returned to allow chaining.

If no argument is provided, it just empties the array and the array object is returned to allow chaining.

When only an offset and/or length are provided, it returns the list of elements found as a regular list (not an object) in non-object context. However, In object context, this returns a new L<Module::Generic::Array> object.

For example:

    my $a = Module::Generic::Array->new( [qw( I disapprove of what you say, but I will defend to the death your right to say it )] );
    say $a->splice( 7, 3 ); # returns a list containing "I", "will", "defend"
    say $a->splice( 7 ); # returns a list containing "I", "will", "defend", "to", "the", "death", "your", "right", "to", "say", "it"

But in object context:

    say a->splice( 7, 3 )->length; # returns 3 obviously
    say a->splice( 7 )->length; # returns 11

Here we use the method "length" but any L<Module::Generic::Array> method workds

=head2 split

Just like the normal L<perlfunct/"split"> function, it takes a string or expression and split the data provided into a list of elements.

It returns the list in list context, and returns a new L<Module::Generic::Array> object in scalar context.

=head2 undef

Just like L</"reset">, this empty the array.

=head2 unique

Uses L<List::Util> to filter out any duplicates, including reference, and return a new L<Module::Generic::Array> object of the resulting array.

Optionally, you can pass it a true value, and it will update the currently used object instead.

    my $dummy = { class => 'Hello' };
    my $a = Module::Generic::Array->new( ['Jack', 'John', $dummy, 'Paul', 'Peter', 'Gabriel', $dummy, 'Peter', 'Raphael', 'Emmanuel'] );
    my $b = $a->unique;
    # $b now contains: 'Jack', 'John', $dummy, 'Paul', 'Peter', 'Gabriel', 'Raphael', 'Emmanuel'

or to update C<$a> directly:

    $a->unique(1);

=head2 unshift

This add the given values at the beginning of the array.

=head2 values

Get a list of all the array values and return a list in list context or a ne L<Module::Generic::Array> object in scalar context.

=head1 SEE ALSO

L<Module::Generic::Scalar>, L<Module::Generic::Number>, L<Module::Generic::Boolean>, L<Module::Generic::Hash>, L<Module::Generic::Dynamic>

=head1 AUTHOR

Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>

=head1 COPYRIGHT & LICENSE

Copyright (c) 2000-2020 DEGUEST Pte. Ltd.

You can use, copy, modify and redistribute this package and associated
files under the same terms as Perl itself.

=cut

