How do I match a list of things in a regex?
I am parsing a file, and its parts are a record, the format is similar:
CategoryA--
5: UserA
6: UserB
7: UserC
CategoryB--
4: UserA
5: UserB
I want to move it to a hash that looks like this:
{ UserA => { CategoryA => 5, CategoryB => 4, },
UserB => { CategoryA => 6, CategoryB => 5, },
UserC => { CategoryA => 7, },
}
How do I make a regular expression?
Edit: it doesn't have to be purely just a regex - only in perl and loops would be fine too.
a source to share
You need two regular expressions, one to define new categories and one to parse user records.
#!/usr/bin/perl
use strict;
use warnings;
my %users;
my $cur;
while (<DATA>) {
if (my ($category) = /^(.*)--$/) {
$cur = $category;
next;
}
next unless my ($id, $user) = /([0-9]+): (\w+)/;
die "no category found" unless defined $cur;
$users{$user}{$cur} = $id;
}
use Data::Dumper;
print Dumper \%users;
__DATA__
CategoryA--
5: UserA
6: UserB
7: UserC
CategoryB--
4: UserA
5: UserB
Or, if you have Perl 5.10 or newer, you can use named entries with a single regex:
#!/usr/bin/perl
use 5.010;
use strict;
use warnings;
my %users;
my $cur;
while (<DATA>) {
next unless /^(?:(?<category>.*)--|(?<id>[0-9]+): (?<user>\w+))$/;
if (exists $+{category}) {
$cur = $+{category};
next;
}
die "no category found" unless defined $cur;
$users{$+{user}}{$cur} = $+{id};
}
use Data::Dumper;
print Dumper \%users;
__DATA__
CategoryA--
5: UserA
6: UserB
7: UserC
CategoryB--
4: UserA
5: UserB
a source to share
This perl code seems to do what you are looking for (with mostly one change). I ignored the data structure a little differently, but not much.
#!/usr/bin/perl
use strict;
my @array = (
"CategoryA--",
"5: UserA",
"6: UserB",
"7: UserC",
"CategoryB--",
"4: UserA",
"5: UserB"
);
my ($dataFileContents, $currentCategory);
for (@array) {
$currentCategory = $1 if (/(Category[A-Z])--/);
if (/(\d+): (User[A-Z])/) {
$dataFileContents->{$2}->{$currentCategory} = $1
}
}
a source to share
Don't really try to play golf here, but it can be done with one alternation:
my ( %data, $category );
while ( <DATA> ) {
next unless /^(?:(Category\w+)|(\d+):\s*(User\w+))/;
( $1 ? $category = $1 : 0 ) or $data{$3}{$category} = $2;
}
Data::Dumper
(actually Smart :: Comments ) shows the result:
{
UserA => {
CategoryA => '5',
CategoryB => '4'
},
UserB => {
CategoryA => '6',
CategoryB => '5'
},
UserC => {
CategoryA => '7'
}
}
a source to share
This will split it into you.
prompt> ruby e.rb
[["CategoryA--", nil, nil], [nil, "5", "UserA"], [nil, "6", "UserB"], [nil, "7", "UserC"], ["CategoryB--", nil, nil], [nil, "4", "UserA"], [nil, "5", "UserB"]]
prompt> cat e.rb
s = <<TXT
CategoryA--
5: UserA
6: UserB
7: UserC
CategoryB--
4: UserA
5: UserB
TXT
p s.scan(/(^.*--$)|(\d+): (.*$)/)
prompt>
a source to share
#!/usr/bin/perl
use strict;
use Data::Dumper;
print "Content-type: text/html\n\n";
my ($x,%data);
do {
if (/^(Category\w+)/) {
$x=$1;
} elsif (/^([0-9]+):\s*(User\w)/) {
if (!defined($data{$2})) {
$data{$2} = {$x,int($1)};
} else {
$data{$2}{$x} = int($1);
}
}
} while (<DATA>);
print Dumper \%data;
__DATA__
CategoryA--
5: UserA
6: UserB
7: UserC
CategoryB--
4: UserA
5: UserB
RESULT:
$VAR1 = {
'UserC' => {
'CategoryA' => 7
},
'UserA' => {
'CategoryA' => 5,
'CategoryB' => 4
},
'UserB' => {
'CategoryA' => 6,
'CategoryB' => 5
}
};
a source to share