Perl Weekly Challenge 110
A couple IO-related challenges this week with nice functional solutions. 🙂
Task 1: Valid Phone Numbers
You are given a text file.
Write a script to display all valid phone numbers in the given text file.
Acceptable Phone Number Formats
+nn nnnnnnnnnn
(nn) nnnnnnnnnn
nnnn nnnnnnnnnn
Input File
0044 1148820341
+44 1148820341
44-11-4882-0341
(44) 1148820341
00 1148820341
Output
0044 1148820341
+44 1148820341
(44) 1148820341
Solution
See below for explanation and any implementation-specific comments.
sub challenge(Str $file-path) returns Str {
$file-path
.IO
.lines
.grep(* ~~ / # [1]
^ # [2]
<space>* # [3]
[\+<digit> ** 2 | \(<digit> ** 2\) | <digit> ** 4] # [4]
<space> # [5]
<digit> ** 10 # [6]
$ # [7]
/)
.join("\n");
}
sub MAIN(Str $file-path) {
say challenge($file-path);
}
This program runs as such:
$ raku ch-1.raku test.txt
0044 1148820341
+44 1148820341
(44) 1148820341
Explanation
The basics here are pretty simple – for each line in the file, check if it matches our regex. If so, keep it, otherwise, drop it. Finally, join all matching lines together using the newline character. See below for comments on the regex itself.
Specific comments
- Spaces in regexes in Raku are insignificant unless we put the
:s
modifier (or “ratchet”) in front of the opening forward slash. This allows us to break the logic up over multiple lines and even add comments. - Match the beginning of the line; this is a universal regex metacharacter.
- Match zero or more spaces. The example file had different levels of space at the beginning of each line, but all of it was insignificant.
- This line matches what I will call our “prefix.” It will match exactly one of the following:
- A literal
+
character follow by two digits. - A literal
(
character followed by two digits followed by a literal)
character. - Four digits in a row.
- A literal
- Each valid phone number had a space between the prefix and the last 10 digits, so this matches a literal space.
- All valid phone numbers end in exactly 10 numbers
- Match the end of the line to verify there is nothing else in the matching line.
Task 2: Transpose File
You are given a text file.
Write a script to transpose the contents of the given file.
Input File
name,age,sex
Mohammad,45,m
Joe,20,m
Julie,35,f
Cristina,10,f
Output
name,Mohammad,Joe,Julie,Cristina
age,45,20,35,10
sex,m,m,f,f
Solution
See below for explanation and any implementation-specific comments.
use Text::CSV; # Imports `csv` function
sub challenge(Str $file-path) returns Str {
my @lines = csv(in => $file-path, headers => "auto"); # [1]
my $names = ('name', |@lines.map(*<name>)).join(','); # [2]
my $ages = ('age', |@lines.map(*<age>)).join(',');
my $sexes = ('sex', |@lines.map(*<sex>)).join(',');
($names, $ages, $sexes).join("\n"); # [3]
}
sub MAIN(Str $file-path) {
say challenge($file-path);
}
This program runs as such:
$ raku ch-2.raku test.csv
name,Mohammad,Joe,Julie,Cristina
age,45,20,35,10
sex,m,m,f,f
Explanation
The bulk of the hard work is done for us by using the Text::CSV
module. It allows us to pull the file into a list of hashes, and then do the logical work on that list. See below for how we apply the logic.
Specific Comments
-
csv
with theheaders
option allows us to pull the CSV file into a list of hashes that looks like this:[ {age => 45, name => Mohammad, sex => m} {age => 20, name => Joe, sex => m} {age => 35, name => Julie, sex => f} {age => 10, name => Cristina, sex => f} ]
- Once we have the list of hashes above, we need three individual strings (one for
name
,age
, andsex
respectively). To do this, we just need to extract the respective key for each item in the list; we can do this via amap
function. Additionally, we prepend the respective key using the("key", |@list)
syntax. Forname
, this is what the output list looks like:(name Mohammad Joe Julie Cristina)
. Finally, we join the output list using commas. - Once we have the 3 key lists, we simply have to join them using the newline character as shown in the example output.
Final Thoughts
I am going through an exercise at my day job where I refactor code from a more imperative approach to a more functional approach. Functional programming should not be used for everything, but when we can use it, it often allows for much more readable and predictable code.