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.
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:
- Removing all spaces and dashes
- Grouping digits into blocks of length 3 from left to right
- 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
- 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
Top comments (0)