DEV Community

Simon Green
Simon Green

Posted on

Weekly Challenge: Similar colors

Weekly Challenge 383

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: Similar List

Task

You are given three list of strings.

Write a script to find out if the first two list are similar with the help the third list. The third list contains the similar words map.

My solution

Lie with a lot of challenges, I can use the examples to extract information that isn't in the task itself. In this case, it's clear from example two that list3 isn't bi-directional. If it was, both lists could match to peach pie and return True.

What isn't clear is the third example where three words appear in the first list in list3. In this case, I've treated word1 word2 word3 as the same as word1 word3 and word2 word3.

For input from the command line, I take comma separated values to make the different lists. The first argument is list1, the second is list2 and the remainder is a list (array in Perl) for list3.

If list1 and list2 are different lengths, I return False. If the two lists are the same, I return True.

def similar_list(list1: list[str], list2: list[str], list3: list[list[str]]) -> bool:
    # Return false if the list1 and list2 are different length
    if len(list1) != len(list2):
        return False

    # ... or they are the same
    if list1 == list2:
        return True
Enter fullscreen mode Exit fullscreen mode

The next step I do is create a mapping dict called mapping (original name, I know :)). This takes the list3 list and creates a mapping from the original word to the target word.

    mapping = {}
    for mapping_list in list3:
        target_word = mapping_list.pop()
        for word in mapping_list:
            mapping[word] = target_word
Enter fullscreen mode Exit fullscreen mode

The final step is to use the dict to convert the two lists, and check for equality.

    target_list1 = [ mapping.get(word, word) for word in list1]
    target_list2 = [ mapping.get(word, word) for word in list2]
    return target_list1 == target_list2
Enter fullscreen mode Exit fullscreen mode

The Perl solution uses the same logic

sub similar_list($list1, $list2, @list3) {
    if ($#$list1 != $#$list2) {
        return 0;
    }

    my %mapping = ();
    foreach my $mapping_list (@list3) {
        my $target_word = pop(@$mapping_list);
        foreach my $word (@$mapping_list) {
            $mapping{$word} = $target_word;
        }}

    my @target_list1 = map { exists $mapping{$_} ? $mapping{$_} : $_} @$list1;
    my @target_list2 = map { exists $mapping{$_} ? $mapping{$_} : $_} @$list2;

    for my $i (0 .. $#$list1) {
        if ($target_list1[$i] ne $target_list2[$i]) {
            return 0;
        }}

    return 1;
}
Enter fullscreen mode Exit fullscreen mode

Examples

$ ./ch-1.py great,acting fine,drama great,fine acting,drama
false

$ ./ch-1.py great,acting fine,drama great,fine acting,drama
true

$ ./ch-1.py apple,pie banana,pie apple,peach peach,banana
false

$ ./ch-1.py perl4,python raku,python perl4,perl5,raku
true

$ ./ch-1.py enjoy,challenge love,weekly,challenge enjoy,love
false

$ ./ch-1.py fast,car quick,vehicle quick,fast vehicle,car
true
Enter fullscreen mode Exit fullscreen mode

Task 2: Nearest RGB

Task

You are given a 6-digit hex color.

Write a script to round the RGB channels to the nearest web-safe value and return the nearest RGB color. Values are 00 (0), 33 (51), 66 (102), 99 (153), CC (204) and FF (255).

My solution

For this task, I have a function called nearest_color that takes a two character hex value and converts to the nearest web safe color. For this I take the average of two values to see the cut off point. For example (255 + 204) รท 2 is 229ยฝ so values greater than or equal to 230 will return FF. In Python (from 3.6) dicts retain their insertion order.

def nearest_color(hex_string: str) -> str:
    """Convert a hex value to the nearest web safe value"""
    mapping = {230: "FF", 179: "CC", 128: "99", 77: "66", 26: "33"}
    value = int(hex_string, base=16)
    for key, result in mapping.items():
        if value >= key:
            return result

    return "00"
Enter fullscreen mode Exit fullscreen mode

Perl hashes on the other hand do not maintain their insertion order. Therefore I need to use the sort function to achieve the same functionality.

sub nearest_color($hex_string) {
    my %mapping =
      ( 230 => "FF", 179 => "CC", 128 => "99", 77 => "66", 26 => "33", );
    my $value = hex($hex_string);
    foreach my $key ( sort { $b <=> $a } keys %mapping ) {
        if ( $value >= $key ) {
            return $mapping{$key};
        }
    }

    return "00";
}
Enter fullscreen mode Exit fullscreen mode

The nearest_rgb function simply checks that input_string is valid (a hash followed by six characters in 0-9 A-F) and calls the nearest_color function for each pair of digits. The Perl code also does the same.

def nearest_rgb(input_string: str) -> str:
    if not re.search(r'^#[0-9A-F]{6}$', input_string):
        raise ValueError(f"Invalid input")

    return ("#"
        + nearest_color(input_string[1:3])
        + nearest_color(input_string[3:5])
        + nearest_color(input_string[5:7])
    )
Enter fullscreen mode Exit fullscreen mode

Examples

$ ./ch-2.py "#F4B2D1"
#FF99CC

$ ./ch-2.py "#15E6E5"
#00FFCC

$ ./ch-2.py "#191A65"
#003366

$ ./ch-2.py "#2D5A1B"
#336633

$ ./ch-2.py "#00FF66"
#00FF66
Enter fullscreen mode Exit fullscreen mode

Top comments (0)