# Perl Weekly Challenge 242: grepping the arrays for fun and profit!

This post presents my solutions to the Perl Weekly Challenge 242.
I keep doing the Perl Weekly Challenge in order to mantain my coding skills in good shape, as well as in order to learn new things, with particular regard to Raku, a language that I love.
This week, I solved the following tasks:
The PL/Perl implementations are very similar to a pure Perl implementation, even if the PostgreSQL environment could involve some more constraints. Similarly, the PL/PgSQL implementations help me keeping my PostgreSQL programming skills in good shape.

# Raku Implementations

## PWC 242 - Task 1 - Raku Implementation

The first task was to take two arrays of integers, and print out which elements of the first array were missing in the second array and vice-versa.

``````sub MAIN( Str \$left, Str \$right ) {

my @left = \$left.split( ',' ).map( *.Int );
my @right = \$right.split( ',' ).map( *.Int );

my ( @missing-left, @missing-right );

@missing-left.push: \$_ if ( ! @left.grep( \$_ ) )  for @right;
@missing-right.push: \$_ if ( ! @right.grep( \$_ ) )  for @left;

( @missing-left, @missing-right ).say;
}

``````

Since Raku allows a postfix `for` with an attached `if`, it becomes quite simple to build up the missing arrays when the `grep` fails to find an element of the first array into the second.

## PWC 242 - Task 2 - Raku Implementation

The second task was about flipping a matrix given as input: each row has to be reversed from left to right and every binary digit has to be negated.

``````sub MAIN() {

my @matrix =  [ 1, 1, 0 ],
[ 0, 1, 1 ],
[ 0, 0, 1 ],
;

my @output;
for @matrix -> \$row {
my \$new-row = \$row.join.flip.comb.map( { \$_ == 0 ?? 1 !! 0 } );
@output.push: [ \$new-row ];
}

@output.join( "\n" ).say;
}

``````

I decided to transform every row of the matrix into a string, to `flip` it, break again into pieces by means of `comb`, and `map` every element negating it. The new row is then placed into an `output` array to be printed out.

# PL/Perl Implementations

## PWC 242 - Task 1 - PL/Perl Implementation

The implementation is the same as in the Raku part, it is only a little more verbose since in Perl you cannot have a postfix `for` with an attached `if`.

``````CREATE OR REPLACE FUNCTION
pwc242.task1_plperl( int[], int[] )
RETURNS TABLE( left int[], right int[] )
AS \$CODE\$
my ( \$left, \$right ) = @_;

my \$missing_left = [];
my \$missing_right = [];

for my \$current ( \$left->@* ) {
push \$missing_right->@*, \$current if ( ! grep( { \$_ == \$current } \$right->@* ) );
}

for my \$current ( \$right->@* ) {
push \$missing_left->@*, \$current if ( ! grep( { \$_ == \$current } \$left->@* ) );
}

return_next( { left => \$missing_left,
right => \$missing_right } );

return undef;
\$CODE\$
LANGUAGE plperl;

``````

## PWC 242 - Task 2 - PL/Perl Implementation

Same approach as the Raku one, where I stringify the matrix row, I flip it and split again to remap to something different.

``````CREATE OR REPLACE FUNCTION
RETURNS int[]
AS \$CODE\$
my ( \$matrix ) = @_;

my \$output;

for my \$row ( \$matrix->@* ) {
push \$output->@*,
[ map { \$_ == 0 ? 1 : 0 } split( //, reverse( join( '', \$row->@* ) ) ) ];

}

return \$output;
\$CODE\$
LANGUAGE plperl;

``````

# PostgreSQL Implementations

## PWC 242 - Task 1 - PL/PgSQL Implementation

A single query to the rescue: I append the output of a first query to the second one.

``````CREATE OR REPLACE FUNCTION
pwc242.task1_plpgsql( left_array int[], right_array int[] )
RETURNS TABLE( which_array text, v int )
AS \$CODE\$
SELECT 'FIRST', la
FROM  unnest( left_array ) la
WHERE la NOT IN ( SELECT unnest( right_array ) )

UNION ALL

SELECT 'SECOND', ra
FROM  unnest( right_array ) ra
WHERE ra NOT IN ( SELECT unnest( left_array ) );
\$CODE\$
LANGUAGE sql;

``````

The idea is that every query extracts from an array only the elements that don’t match the other array. I use a text column to indicate from where the result comes from.

## PWC 242 - Task 2 - PL/PgSQL Implementation

In this implementation I iterate over every row of the matrix and build an array of the reversed values to return at each iteration.

``````CREATE OR REPLACE FUNCTION
pwc242.task2_plpgsql( matrix int[], l int )
RETURNS SETOF int[]
AS \$CODE\$
DECLARE
current_row text;
current int;
index int;
to_return int;
result int[];
BEGIN
index := 1;
current_row := '';

FOREACH current IN ARRAY matrix LOOP
current_row := current_row || current::text;
IF index = l THEN
-- the row is now complete, flip and split
result := array[]::int[];
FOREACH to_return IN ARRAY regexp_split_to_array( reverse( current_row ), '' ) LOOP
IF to_return = 1 THEN
result := array_append( result, 0 );
ELSE
result := array_append( result, 1 );
END IF;
END LOOP;

RETURN NEXT result;

-- start over
current_row := '';
index := 1;
ELSE
index := index + 1;
END IF;
END LOOP;

RETURN;
END
\$CODE\$
LANGUAGE plpgsql;

``````

Note that, since PL/PgSQL does not allow for multidimensional array, I have to split the incoming monodimensional array into a matrix by meaning of an incoming argument `l` that represents the length of the array. I still stringify the initial row content and use `reverse` to flip it, then splitting by means of `regexp_split_to_array`.

# Python Implementations

## PWC 242 - Task 1 - Python Implementation

A really verbose implementation, because of the need to extract the two arrays from the command line arguments. I assume that every array is divided by a `'|'` on the command line, so the first loop is used only to build the arrays.

``````import sys

def main( argv ):
left = []
right = []
is_left = True
for current in argv:
if current != '|':
if ( is_left ):
left.append( int( current ) )
else:
right.append( int( current ) )
else:
is_left = False

missing_left = []
missing_right = []

for current in left:
if not current in right:
missing_left.append( current )

for current in right:
if not current in left:
missing_right.append( current )

print( ",".join( map( str, missing_left ) ) )
print( ",".join( map( str, missing_right ) ) )

# invoke the main without the command itself
if __name__ == '__main__':
main( sys.argv[ 1: ] )

``````

Python does not have a `grep` like function, but the `in` operator does what you could expect and checks the presence of an alement in the other array.

## PWC 242 - Task 2 - Python Implementation

Similarly to the previous one, I assume that a `'|'` on the command line arguments means that the matrix row is complete, so in the beginning I loop to build up the matrix multidimensional array, Then I define a `task2` function that, given an input number, returns the string representing the binary reversed (negated) value. I return a string to make `join` happier later on. Last, I use `map` with my defined function to remap the `reversed` array representing a single row and print it out.

``````import sys

def main( argv ):
matrix = []
current_row = 0

matrix.append( [] )
for current in argv:
if current != '|':
matrix[ current_row ].append( int( current ) )
else:
current_row += 1
matrix.append( [] )

# inner function to use with map
# returns a string to make join happy!
if n == 1:
return "0"
else:
return "1"

for current_row in matrix:
print( ",".join( map( task2, reversed( current_row ) ) ) )

# invoke the main without the command itself
if __name__ == '__main__':
main( sys.argv[ 1: ] )

``````

The article Perl Weekly Challenge 242: grepping the arrays for fun and profit! has been posted by Luca Ferrari on November 6, 2023