Weekly Challenge: The one about formatting

Weekly Challenge 347

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. It’s a great way for us all to practice some coding.

Challenge, My solutions

Task 1: Format Date

Task

You are given a date in the form: 10th Nov 2025.

Write a script to format the given date in the form: 2025-11-10 using the sets below.

@DAYS   = ("1st", "2nd", "3rd", ....., "30th", "31st")
@MONTHS = ("Jan", "Feb", "Mar", ....., "Nov",  "Dec")
@YEARS  = (1900..2100)

My solution

Both of these weeks tasks are pretty straight forward so don’t require too much explanation. For this task, I start by defined the lists (arrays in Perl) for DAYS, MONTHS and YEARS. As the years are actually a string, I use the map function to convert them to a string.

DAYS = [
    "1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th",
    "11th", "12th", "13th", "14th", "15th", "16th", "17th", "18th", "19th",
    "20th", "21st", "22nd", "23rd", "24th", "25th", "26th", "27th", "28th",
    "29th", "30th", "31st"
]

MONTHS = [
    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
    "Nov", "Dec"
]

YEARS = list(map(str, range(1900, 2101)))

I start the function by defining the three fields. Each list is the description of the field, the list to use, and the offset value.

def format_date(input_string: str) -> str:
    fields = [
        ("day of month", DAYS, 1),
        ("month", MONTHS, 1),
        ("year", YEARS, 1900)
    ]

I then split the input on spaces and store this as input_list. I create an empty list called output_list. I also check that there are three items in the list.

    input_list = input_string.split()
    output_list = []

    if len(input_list) != 3:
        raise ValueError("Input must contain day, month, and year")

I then loop through each field, getting the index (position) of the part of index_list that we are looking at, and check it is in the list. If it isn’t, I’ll raise an error (e.g. “Invalid day of month: 23th”). The offset is applied and the value is added to the output_list list. I use an offset as ‘Jan’ is at position zero, but it is the 1st month (as written as a date).

    for i in (range(3)):
        name, values, offset = fields[i]
        value = input_list[i]
        if value not in values:
            raise ValueError(f"Invalid {name}: {value}")
        index = values.index(value) + offset
        output_list.append(f"{index:02d}")

Finally, I print the date. I reverse the list and separate it with dashes.

    return "-".join(reversed(output_list))

The Perl code follows the same logic.

Examples

$ ./ch-1.py "1st Jan 2025"
2025-01-01

$ ./ch-1.py "22nd Feb 2025"
2025-02-22

$ ./ch-1.py "15th Apr 2025"
2025-04-15

$ ./ch-1.py "23rd Oct 2025"
2025-10-23

$ ./ch-1.py "31st Dec 2025"
2025-12-31

Task 2: Format Phone Number

Task

You are given a phone number as a string containing digits, space and dash only.

Write a script to format the given phone number using the below rules:

  1. Removing all spaces and dashes
  2. Grouping digits into blocks of length 3 from left to right
  3. Handling the final digits (4 or fewer) specially:
    • 2 digits: one block of length 2
    • 3 digits: one block of length 3
    • 4 digits: two blocks of length 2
  4. Joining all blocks with dashes

My solution

For this task, I start by using a regular expression removing anything that isn’t a digit from the input_string variable. I create an empty list called parts.

I use a while loop that runs if input_string is not empty. I then determine how many characters to remove from the front of the string. If the string is four characters long, I set this to 2. In other cases, I set it to the minimum of 3 or the length of the string. I remove the characters from the start of the string and add it to the parts list. The loop runs again until all characters are removed.

def format_phone(input_string: str) -> str:
    # Strip all non-digit characters
    input_string = re.sub(r'D', '', input_string)

    parts = []
    while input_string:
        # Decide length of next part
        l = 2 if len(input_string) == 4 else min(3, len(input_string))
        parts.append(input_string[:l])
        input_string = input_string[l:]

    return '-'.join(parts)

The Perl solution follows the same logic. It uses the substr function which can both remove the leading characters and add to the parts list in a single call.

sub main ($input_string) {
    # Strip all non-digit characters
    $input_string =~ s/D//g;

    my @parts = ();
    while ($input_string) {
        # Decide length of next part
        my $l = length($input_string) == 4 ? 2 : min( 3, length($input_string) );
        push @parts, substr( $input_string, 0, $l, "" );
    }

    say join( "-", @parts );
}

Examples

$ ./ch-2.py "1-23-45-6"
123-456

$ ./ch-2.py "1234"
12-34

$ ./ch-2.py "12 345-6789"
123-456-789

$ ./ch-2.py "123 4567"
123-45-67

$ ./ch-2.py "123 456-78"
123-456-78
Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post

Seamless Map Integration in React Native: A Complete Guide

Related Posts