2

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";
0

4 Answers 4

2

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
Sign up to request clarification or add additional context in comments.

Comments

1

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) {"

2 Comments

You do not need to initialize a scalar value into an (empty) array ref, it is done automatically.
@TLP - true, but it's a bit of an unintuitive magic to a total newbie.
1

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

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.