DEV Community

Simon Green
Simon Green

Posted on

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)
Enter fullscreen mode Exit fullscreen mode

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)))
Enter fullscreen mode Exit fullscreen mode

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)
    ]
Enter fullscreen mode Exit fullscreen mode

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")
Enter fullscreen mode Exit fullscreen mode

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}")
Enter fullscreen mode Exit fullscreen mode

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

    return "-".join(reversed(output_list))
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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 );
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Top comments (0)