# 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:
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 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 of `24` (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 module `Algorithm::Combinatorics`, so the implementation must be done in `plperlu`.

``````CREATE OR REPLACE FUNCTION
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
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
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
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 {

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 {

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();
if ( ! frequency.get( freq ).contains( 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 to `itertools` this can be solved in one line (or so).

``````import sys
from itertools import combinations

# the return value will be printed
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

# the return value will be printed
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: ] ) )

``````

The article Perl Weekly Challenge 276: filtering arrays has been posted by Luca Ferrari on July 3, 2024