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";
È stato utile?

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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top