DEV Community

Simon Green
Simon Green

Posted on

Weekly Challenge: A different average

Weekly Challenge 398

Each week Mohammad S. Anwar sends out The Weekly Challenge, a chance for all of us to come up with solutions to two weekly tasks. My solutions are written in Python first, and then converted to Perl. Unless otherwise stated, Copilot (and other AI tools) have NOT been used to generate the solution. It's a great way for us all to practice some coding.

Challenge, My solutions

Task 1: Divisible Average

You are given an array of positive integers.

Write a script to return the closest integer to the mean value of all even integers that are divisible by 3. If none is divisible, return 0.

My solution

An even number that is divisible by 3, means it must be divisible by 6. I create a list (array in Perl) called eligible_ints which has all integers that match this critera.

If the list is empty (i.e. there are no integers divisble by 6), I return 0. Otherwise I return the nearest integer. Python's round function uses "bankers rounding" when half values round to the nearest even number.

def divisible_average(ints: list[int]) -> int:
    eligible_ints = [i for i in ints if i % 6 == 0]

    if not eligible_ints:
        return 0

    return round(sum(eligible_ints) / len(eligible_ints))
Enter fullscreen mode Exit fullscreen mode

The Perl solution uses the same logic. This round half values upwards.

sub main (@ints) {
    my @eligible_ints = grep { $_ % 6 == 0 } @ints;

    if ( scalar(@eligible_ints) ) {
        say sprintf "%.0f", sum(@eligible_ints) / scalar(@eligible_ints);
    }
    else {
        say 0;
    }
}
Enter fullscreen mode Exit fullscreen mode

Examples

$ ./ch-1.py 18 5 7 11
18

$ ./ch-1.py 6 12 18 24 30 40
18

$ ./ch-1.py 3 9 15 21 27
0

$ ./ch-1.py 6 12 14 20
9

$ ./ch-1.py 6 6 6 12 12
8
Enter fullscreen mode Exit fullscreen mode

Task 2: String Difference

You are given an array of equal length strings.

Write a script to find the difference integer array of each word in the given array. There will be one word with a different array; return that word.

Each words[i] can be converted into a difference integer array difference[i] of length n - 1 where difference[i][j] = words[i][j+1] - words[i][j]

My solution

I start this task by checking all words are of the same length.

from collections import defaultdict

def string_difference(words: list[str]) -> str:
    if any(len(word) != len(words[0]) for word in words):
        raise ValueError("Words must be of the same length")
Enter fullscreen mode Exit fullscreen mode

I then create a dict called word_difference which starts by checking if the word is made up of lower case letters of the English alphabet. I then create a tuple of the differences in each letter. As a tuple is immutable, it can be used as a key in a dict. The defaultdict is a special dictionary that will auto-create the key if it does not exist.

    word_difference = defaultdict(list)
    for word in words:
        if not re.search("^[a-z]+$", word):
            raise ValueError(f"The word {word} is not all lower case English letters")

        t = tuple(ord(word[i]) - ord(word[i - 1]) for i in range(1, len(word)))
        word_difference[t].append(word)
Enter fullscreen mode Exit fullscreen mode

I then find all words that have a unique key.

    unique_words = [
        word_list[0] for word_list in word_difference.values() if len(word_list) == 1
    ]
Enter fullscreen mode Exit fullscreen mode

If there is only one word, I return it. If there are zero or two or more, I raise an exception.

    if len(unique_words) == 1:
        return unique_words[0]

    raise ValueError(f"There were {len(unique_words)} words with unique differences")
Enter fullscreen mode Exit fullscreen mode

The Perl solution follows a similar pattern. As Perl cannot use an array as a key to a hash, I join the @differences value with a space to generate the key.

use List::Util 'any';

sub main (@words) {
    my %word_difference = ();

    if ( any { length($_) != length( $words[0] ) } @words ) {
        die "Words must be of the same length\n";
    }

    foreach my $word (@words) {
        # Check words only contain lower case letters
        if ( $word !~ /^[a-z]+$/ ) {
            die "The word $word is not all lower case English letters\n";
        }

        my @differences = map {
            ord( substr( $word, $_, 1 ) ) - ord( substr( $word, $_ - 1, 1 ) )
        } ( 1 .. length($word) - 1 );
        push @{ $word_difference{ join( " ", @differences ) } }, $word;
    }

    my @unique_words =
      map { $_->[0] } grep { scalar(@$_) == 1 } values(%word_difference);
    if ( scalar(@unique_words) == 1 ) {
        say $unique_words[0];
    }
    else {
        say "There were ", scalar(@unique_words),
          " words with unique differences";
    }
}
Enter fullscreen mode Exit fullscreen mode

Examples

$ ./ch-2.py ab bc cd zy
zy

$ ./ch-2.py cba fed azy
azy

$ ./ch-2.py pop ada beb cfc
pop

$ ./ch-2.py aaaa zzzz mmmm aaba
aaba

$ ./ch-2.py aceg bdfh cegi azbz
azbz
Enter fullscreen mode Exit fullscreen mode

Top comments (0)