Perl Weekly Challenge 276: filtering arrays
This post presents my solutions to the Perl Weekly Challenge 276.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:
- PWC 276 - Task 1 - Raku
- PWC 276 - Task 2 - Raku
- PWC 276 - Task 1 in PostgreSQL PL/Perl
- PWC 276 - Task 2 in PostgreSQL PL/Perl
- PWC 276 - Task 1 in PostgreSQL PL/PgSQL
- PWC 276 - Task 2 in PostgreSQL PL/PgSQL
- PWC 276 - Task 1 in PL/Java
- PWC 276 - Task 2 in PL/Java
- PWC 276 - Task 1 in Python
- PWC 276 - Task 2 in Python
Raku Implementations
PWC 276 - Task 1 - Raku Implementation
The first task was about finding all the couples of a given list of integers so that the sum of every couple is a multiple of24
(hours).
This can be solved with a one line.
sub MAIN( *@hours where { @hours.elems == @hours.grep( * ~~ Int ) } ) {
@hours.combinations( 2 ).grep( { ( $_[ 0 ] + $_[ 1 ] ) %% 24 } ).say;
}
The
combinations( 2 )
provides me all the possible combinations of two elements, therefore a couple, and then I keep by means of grep
only thos that have a sum that is a multiple of 24
.
PWC 276 - Task 2 - Raku Implementation
Given an array of integers, tell how many elements have the same max frequency.sub MAIN( *@nums where { @nums.elems == @nums.grep( { $_ ~~ Int && $_ > 0 } ).elems } ) {
my %frequency;
%frequency{ @nums.grep( * ~~ $_ ).elems }.push: $_ for @nums;
%frequency{ %frequency.keys.max }.unique.elems.say;
}
First of all, I classify the
%frequency
of every element, then extract the max value of the keys (frequency), pass thru unique
to exclude duplicates, and count the number of elems
.
PL/Perl Implementations
PWC 276 - Task 1 - PL/Perl Implementation
I need to use an external moduleAlgorithm::Combinatorics
, so the implementation must be done in plperlu
.
CREATE OR REPLACE FUNCTION
pwc276.task1_plperl( int[] )
RETURNS SETOF int[]
AS $CODE$
use Algorithm::Combinatorics qw/ combinations /;
my ( $hours ) = @_;
my $iterator = combinations( \ $hours->@*, 2 );
while( my $c = $iterator->next ) {
return_next( $c ) if ( ( $c->@[ 0 ] + $c->@[ 1 ] ) % 24 == 0 );
}
return undef;
$CODE$
LANGUAGE plperlu;
I return every couple of the
combinations
that satisfies the 24
sum multiple.
PWC 276 - Task 2 - PL/Perl Implementation
The approach is the same as in Raku, only more verbose.CREATE OR REPLACE FUNCTION
pwc276.task2_plperl( int[] )
RETURNS int
AS $CODE$
my ( $nums ) = @_;
die "Need only positives" if ( grep { $_ <= 0 } $nums->@* );
my $frequency = {};
for my $current ( $nums->@* ) {
my $count = scalar grep { $_ == $current } $nums->@*;
push $frequency->{ $count }->@*, $current if ( ! grep { $_ == $current } $frequency->{ $count }->@* );
}
my $max_frequency = ( sort( { $b <=> $a } keys $frequency->%* ) )[ 0 ];
return scalar $frequency->{ $max_frequency }->@*;
$CODE$
LANGUAGE plperl;
First I build the
$frequency
hash with the frequencies as keys and the list of unique elements as the values.
Then I compute the $max_frequency
using sort
and limiting the number of results, and last I compute how many elements appear with such frequency.
PostgreSQL Implementations
PWC 276 - Task 1 - PL/PgSQL Implementation
A single query does suffice!CREATE OR REPLACE FUNCTION
pwc276.task1_plpgsql( hours int[] )
RETURNS TABLE( l int, r int )
AS $CODE$
WITH elems AS ( SELECT v::int, row_number() OVER ( ORDER BY v ) AS r
FROM unnest( hours ) v
)
SELECT l.v::int, r.v::int
FROM elems l, elems r
WHERE mod( ( l.v::int + r.v::int ), 24 ) = 0
AND l.r < r. r
;
$CODE$
LANGUAGE sql;
The idea is to produce a materialization of the array elements with a row numbering, so that I can join the materialization against itself and get only those pairs that produce a modulo
24
value assuming the row number of one is greater than the other (to skip self-sums).
PWC 276 - Task 2 - PL/PgSQL Implementation
Again, a single query!CREATE OR REPLACE FUNCTION
pwc276.task2_plpgsql( nums int[] )
RETURNS int
AS $CODE$
WITH freq AS (
SELECT count( v ) as frequency, v
FROM unnest( nums ) v
GROUP BY v
)
, max_freq AS ( SELECT frequency FROM freq
ORDER BY 1 DESC
LIMIT 1
)
SELECT count( f )
FROM freq f
WHERE f.frequency = ( SELECT frequency FROM max_freq )
;
$CODE$
LANGUAGE sql;
First I materialize the
freq
list of frequency and values. Then, on top this, I build the max_freq
present in the above list.
Last, I count how many rows I have for the the max_freq
.
Java Implementations
PWC 276 - Task 1 - PostgreSQL PL/Java Implementation
A boring nested loop to solve this. public static final String[] task1_pljava( int[] hours ) throws SQLException {
logger.log( Level.INFO, "Entering pwc276.task1_pljava" );
List<String> result = new LinkedList<String>();
for ( int i = 0; i < hours.length - 1; i++ )
for ( int j = i + 1; j < hours.length; j++ )
if ( ( hours[ i ] + hours[ j ] ) % 24 == 0 )
result.add( String.format( "%02d + %02d", hours[ i ], hours[ j ] ) );
return result.toArray( new String[ 0 ] );
}
I return a
String[]
just to avoid the cluttering of implementing a complex ResultProvider
.
PWC 276 - Task 2 - PostgreSQL PL/Java Implementation
Using the Stream API this can be solved in a more compact way. public static final int task2_pljava( int[] nums ) throws SQLException {
logger.log( Level.INFO, "Entering pwc276.task2_pljava" );
final Map<Integer, List<Integer>> frequency = new HashMap<Integer, List<Integer>>();
Arrays.stream( nums )
.forEach( v -> {
int freq = (int) Arrays.stream( nums ).filter( k -> k == v ).count();
frequency.putIfAbsent( freq, new LinkedList<Integer>() );
if ( ! frequency.get( freq ).contains( v ) )
frequency.get( freq ).add( v );
} );
final int[] max = new int[]{ 0, 0 };
// find out the max frequency
frequency.keySet().stream().forEach( k -> {
if ( k > max[ 0 ] ) {
max[ 0 ] = k;
max[ 1 ] = frequency.get( k ).size();
}
} );
return max[ 1 ];
}
The first iteration with
forEach
is done to map into frequency
the frequency found and the list of values that have such frequency.
The second forEach
is done over the keys of the Map
(i.e., the frequencies) to find the greatest one and store into max
, along with the length of the values.
Python Implementations
PWC 276 - Task 1 - Python Implementation
Thanks toitertools
this can be solved in one line (or so).
import sys
from itertools import combinations
# task implementation
# the return value will be printed
def task_1( args ):
hours = list( map( int, args ) )
return list( filter( lambda v: ( v[ 0 ] + v[ 1 ] ) % 24 == 0, list( combinations( hours, 2 ) ) ) )
# invoke the main without the command itself
if __name__ == '__main__':
print( task_1( sys.argv[ 1: ] ) )
PWC 276 - Task 2 - Python Implementation
The implementation is really similar to the PL/Perl one, except that I keep track of the max frequency while iterating to avoid a filtering on the dictionary keys.import sys
# task implementation
# the return value will be printed
def task_2( args ):
nums = list( map( int, args ) )
frequency = {}
max_freq = 0
for x in nums:
freq = len( list( filter( lambda v: v == x, nums ) ) )
if freq > max_freq:
max_freq = freq
if not freq in frequency:
frequency[ freq ] = []
if not x in frequency[ freq ]:
frequency[ freq ].append( x )
return len( frequency[ max_freq ] )
# invoke the main without the command itself
if __name__ == '__main__':
print( task_2( sys.argv[ 1: ] ) )