This is my response to the Perl Weekly Challenge #110.
+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
Note the discrepancy between the first rule (plus, digit, digit, space, space, …) and the values given in the in- and output (space, plus, digit, digit, space, …). I am assuming that the latter is correct.
We need a file with the phone numbers. Here it is:
File: phone-numbers.txt
0044 1148820341
+44 1148820341
44-11-4882-0341
(44) 1148820341
00 1148820341
The program is quite straight forward:
File: valid-phone-numbers
#! /usr/bin/env raku
unit sub MAIN ($phonebook where $phonebook.IO.f && $phonebook.IO.r # [1]
= 'phone-numbers.txt');
for $phonebook.IO.lines -> $line # [2]
{
next unless $line.chars == 15; # [3]
next unless $line.substr(4,1) eq ' '; # [4]
next unless $line.substr(5) ~~ /^\d ** 10$/; # [5]
my $first = $line.substr(0,4); # [6]
next unless $first ~~ /^\d ** 4$/ || # [7]
$first ~~ /^\s\+\d\d$/ || # [7a]
$first ~~ /^\(\d\d\)$/; # [7b]
say $line; # [8]
}
[1]
Ensure that we get a file name (IO.f
), and that it is
readable (IO.r
). Note the default file name.
[2] Iterate over the lines in the file.
[3] Skip lines that do not have the correct length of 15 characters.
[4] Skip lines where the fifth character (at offset 4) is anything besides a space character.
[5] Skip lines where the last part (after the space mentioned in [4]) does not consist of digits only.
[6] We are considering the first four characters next. Pick them out as a string.
[7] Skip lines where none of the regular expressions are satisifed. The first on looks for four digits [7], the second for a space, a plus and two digits [7a], and the third for two dogots inside a pair of parens [7b].
[8] It must be legal; print it.
See docs.raku.org/routine/f and docs.raku.org/routine/r for more information about those file tests.
Running it gives the expected result:
$ ./valid-phone-numbers
0044 1148820341
+44 1148820341
(44) 1148820341
#! /usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use File::Slurp;
my $phonebook = $ARGV[0] // 'phone-numbers.txt';
for my $line (read_file($phonebook, chomp => 1))
{
next unless length($line) == 15;
next unless substr($line, 4, 1) eq ' ';
next unless substr($line, 5) =~ /^\d{10}$/;
my $first = substr($line, 0, 4);
next unless $first =~ /^\d{4}$/ ||
$first =~ /^\s\+\d\d$/ ||
$first =~ /^\(\d\d\)$/;
say $line;
}
Running it gives the same result as the Raku version:
$ ./valid-phone-numbers-perl
0044 1148820341
+44 1148820341
(44) 1148820341
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
This is just a matter of reading the lines, putting the words in separate lists as we do som, and printing those list at the end.
File: transpose-file
#! /usr/bin/env raku
unit sub MAIN ($csv-file where $csv-file.IO.f && $csv-file.IO.r
= 'persons.csv');
my @lines;
for $csv-file.IO.lines -> $line
{
my @words = $line.split(','); # [1]
for 0 .. @words.end -> $index # [2]
{
@lines[$index].push: @words[$index]; # [3]
}
}
for @lines -> $line # [4]
{
say $line.join(','); # [4a]
}
[1] Split the line on comma.
[2] Iterate over the index in the list of words we just got.
[3] Add the word to the sublist with that index.
[4] For each element in the list, print the values (the sublist).
Running it:
./transpose-file
name,Mohammad,Joe,Julie,Cristina
age,45,20,35,10
sex,m,m,f,f
Looking good.
Note that the challenge does not say what to do if the number of words (commas) on the lines differ. So I have ignored it.
#! /usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use File::Slurp;
my $csv_file = $ARGV[0] // 'persons.csv';
my @lines;
for my $line (read_file($csv_file, chomp => 1))
{
my @words = split(',', $line);
for my $index (0 .. @words -1)
{
$lines[$index] .= $lines[$index] # [1]
? ( "," . $words[$index]) # [1a]
: $words[$index]; # [1b]
}
}
say $_ for @lines; # [2]
[1] Lists of lists require pointers in Perl. It is easier to use strings. If we add a value, prefix it with a comma [1a]. If not, just add it.
[2] Much shorter, as we just print a bunch of strings.
Running it gives the same result as the Raku version:
$ ./transpose-file-perl
name,Mohammad,Joe,Julie,Cristina
age,45,20,35,10
sex,m,m,f,f
And that's it.