The Weekly Challenge - 284

TASK #1: Lucky Integer
You are given an array of integers, @ints.

Write a script to find the lucky integer if found otherwise return -1. If there are more than one then return the largest.

A lucky integer is an integer that has a frequency in the array equal to its value.

#!/usr/bin/perl
use strict;
use warnings;
use Statistics::Frequency;

=begin
Dive back again into the Statistics::Frequency module - because we clearly can't get enough of it
=cut

sub lucky_integer {
    my @ints = @_;

    # return -1 if the input array is empty
    return(-1) unless @ints;

    # create a new Statistics::Frequency object    
    my $freq = Statistics::Frequency->new(@ints);
    
    # filter the list to only include elements where the
    # frequency equals the element's value
    my @list = grep { $_ == $freq->frequency($_) } @ints;
    
    # sort the filtered list in descending order
    @list = sort { $b <=> $a } @list;

    # return the first element (largest) or -1 if the list is empty
    return(@list ? $list[0] : -1);
}

# Tests

my @ints;

# Example 1
@ints = (2, 2, 3, 4);
print(lucky_integer(@ints), "\n"); # Output: 2

# Example 2
@ints = (1, 2, 2, 3, 3, 3);
print(lucky_integer(@ints), "\n"); # Output: 3

# Example 3
@ints = (1, 1, 1, 3);
print(lucky_integer(@ints), "\n"); # Output: -1
Task #2: Relative Sort
You are given two list of integers, @list1 and @list2. The elements in the @list2 are distinct and also in the @list1.

Write a script to sort the elements in the @list1 such that the relative order of items in @list1 is same as in the @list2. Elements that is missing in @list2 should be placed at the end of @list1 in ascending order.
#!/usr/bin/perl
use strict;
use warnings;

=begin
Just straightforward coding, easily following the task description.
=cut

sub relative_sort {
    
    # retrieve the first list from the reference
    my @list1 = @{ shift() };  
    # retrieve the second list from the reference    
    my @list2 = @{ shift() };  

    # create a hash map for the positions of elements in @list2
    my %element_order_list2 = map { $list2[$_] => $_ } 0 .. $#list2;

    # separate @list1 elements into those in @list2 and those not in @list2
    my (@in_list2, @not_in_list2);
    foreach ( @list1 ) {
        ( exists $element_order_list2{$_} ) ? push(@in_list2, $_) 
                                            : push(@not_in_list2, $_);
    }  

    # sort @in_list2 according to the order in @list2
    @in_list2 = sort { $element_order_list2{$a} <=> $element_order_list2{$b} } @in_list2;

    # sort @not_in_list2 in ascending order
    @not_in_list2 = sort { $a <=> $b } @not_in_list2;

    # combine both lists
    return(@in_list2, @not_in_list2);
}

# Tests

my (@list1, @list2);

# Example 1
@list1 = (2, 3, 9, 3, 1, 4, 6, 7, 2, 8, 5);
@list2 = (2, 1, 4, 3, 5, 6);
print("(", join(", ", relative_sort(\@list1, \@list2)), ")\n"); # Output: (2, 2, 1, 4, 3, 3, 5, 6, 7, 8, 9)

# Example 2
@list1 = (3, 3, 4, 6, 2, 4, 2, 1, 3);
@list2 = (1, 3, 2);
print("(", join(", ", relative_sort(\@list1, \@list2)), ")\n"); # Output: (1, 3, 3, 3, 2, 2, 4, 4, 6)

# Example 3
@list1 = (3, 0, 5, 0, 2, 1, 4, 1, 1);
@list2 = (1, 0, 3, 2);
print("(", join(", ", relative_sort(\@list1, \@list2)), ")\n"); # Output: (1, 1, 1, 0, 0, 3, 2, 4, 5)