making arrays from tab-delimited text file column
-
31-05-2021 - |
Domanda
I was wondering if anyone could help a desperate newbie with perl with the following question. I've been trying all day but with my perl book at work, I can't seem to anything relevant in google...or maybe am genuinely stupid with this.
I have a file that looks something like the following:
Bob April
Bob April
Bob March
Mary August
Robin December
Robin April
The output file I'm after is:
Bob April April March
Mary August
Robin December April
So that it lists each month in the order that it appears for each person.
I tried making it into a hash but of course it wouldn't let me have duplicates so I thought I would like to have arrays for each name (in this example, Bob, Mary and Robin). I'm afraid to upload the code I've been trying to tweak because I know it'll be horribly wrong. I think I need to define(?) the array. Would this be correct?
Any help would be greatly appreciated and I promise I will be studying more about perl in the meantime.
Thank you for your time, patience and help.
#!/usr/bin/perl -w
while (<>) {
chomp;
if (defined $old_name) {
$name=$1;
$month=$2;
if ($name eq $old_name) {
$array{$month}++;
}
else {
print "$old_name";
foreach (@array) {
push (@array, $month);
print "\t@array";
}
print "\n";
@array=();
$array{$month}++;
}
}
else {
$name=$1;
$month=$2;
$array{month}++;
}
$old_name=$name;
}
print "$old_name";
foreach (@array) {
push (@array, $month);
print "\t@array";
}
print "\n";
Soluzione
Your code looks overly complicated for such a simple task.
use strict;
use warnings;
my %hash;
while (<DATA>) {
my ($name, $mon) = split;
push @{$hash{$name}}, $mon;
}
for my $name (keys %hash) {
my @months = @{$hash{$name}};
print "$name\t@months\n";
}
__DATA__
Bob April
Bob April
Bob March
Mary August
Robin December
Robin April
Altri suggerimenti
You are somewhat close. You do want to use a hash with names being the key, but as you can see, for each name you want to store an array of months, so the data structure you are looking to use is a hash of arrays (or rather array references, as this is implemented in Perl)
While at it, please don't get into the habit of using global variables - 100% of your code should have "use strict; use warnings;
" at the beginning, and locally scoped (my
) variables.
use strict;
my %data;
my @sorted_names; # Only needed if you care which order to print the results
while (<>) {
chomp;
my ($name, $month) = split(/s+/);
if (! $data{$name}) {
# Initialize to empty array reference if first time.
# Not required - perl will do it for you
$data{$name} ||= [];
# Only needed if you want to print results in the same order of names as input.
push @sorted_names, $name;
}
push @{ $data{$name} }, $month;
}
foreach my $name (@sorted_names) {
print "$name\t" . join(" ", @{ $data{$name} }) . "\n";
}
# If don't care about name order, just do "foreach my $name (keys %data) {"
Script:
#!/usr/bin/perl
use strict;
use warnings;
my %content;
open my $fh, '<file.txt' or die $!;
while (<$fh>) {
push @{$content{$1}}, $2 if /^(\S+)\s+(\S+)\s*$/;
}
close $fh;
foreach (keys %content) {
print $_, "\t";
foreach my $item (@{$content{$_}}) {
print "$item ";
}
print "\n";
}
or
#!/usr/bin/perl
use strict;
use warnings;
my %content;
open my $fh, '<file.txt' or die $!;
while (<$fh>) {
push @{$content{$1}}, $2 if /^(\S+)\s+(\S+)\s*$/;
}
close $fh;
print "$_\t@{$content{$_}}\n" for keys %content;
or
#!/usr/bin/perl
use strict;
use warnings;
my %content;
open my $fh, '<file.txt' or die $!;
s/^(\S+)\s+(\S+)\s*$/{push @{$content{$1}}, $2}/e for <$fh>;
close $fh;
print "$_\t@{$content{$_}}\n" for keys %content;
Output:
Bob April April March
Mary August
Robin December April
for file file.txt
with content:
Bob April
Bob April
Bob March
Mary August
Robin December
Robin April
an easy way to do this is using perl's push and pop functions.(since you are getting started with perl: http://perldoc.perl.org/functions/pop.html , http://perldoc.perl.org/functions/push.html ) you should keep a global array for each name(eg @bobmonths) and push a month each time you find one. When done, print out the name and the array:
while(<>)
{
chomp;
push(@bobmonths, $2)
...
}
print @bobmonths