EmmittSmith has asked for the wisdom of the Perl Monks concerning the following question:

Hey, I want this program to take a document of names (the first name, a space, and then the last name), which are seperated by a newline, and sort them alphabetically by their last name, printing out their last name, a comma, and their first... But, for some reason, in my foreach loop it will only print one name, instead of all of the names...
Here's the code:

#!/usr/bin/perl -w use strict; open(TEST, 'test.txt') || die "Couldn't open it"; print "\nFile Opened\n\n"; my $i=1; my %list; while(<TEST>){ chomp($_); $_ = /(\w*)\s?(\w*)\n?/; print "$i : $2\,$1\n"; %list = ("$i" => ["$1","$2"]); $i+=1; } foreach $a(sort(keys(%list))){print "$list{$a}[1]\,$list{$a}[0]\n"} close(TEST); die "\n\nFile Closed\n";

test.txt:

Emmitt Smith
Walter Payton
Barry Sanders
Jim Brown

Thanks,
Emmitt

Replies are listed 'Best First'.
Re: Associative Array Trouble
by Zaxo (Archbishop) on May 22, 2002 at 23:43 UTC

    Update: You have a terrible error that's one of my hobbyhorses, and I missed it on the first cut :( $a is sacred to sort

    You're assigning a whole new %list each time around.

    while(<TEST>){ chomp; /(\w+)\s+(\w+)/; print "$i : $2\,$1\n"; $list{$i} = [$1,$2]; $i+=1; }
    Also, when you want the values, you need to should dereference:
    foreach my $c (sort keys %list) { print "$list{$c}->[1],$list{$c}->[0]$/" }
    That should fix things up.

    Update: Softened statement on dereferencing and corrected horrible error mixing $a as temporary with sort.

    Update 2: Here's a line to sort and print all at once

    print map {"$_->[1], $_->[0]$/"} sort {lc($a->[1]) cmp lc($b->[1])} va +lues %list;
    You might also consider split instead of re+backreferences for parsing your data, more efficient. Edit, corrected typo of dropped paren

    After Compline,
    Zaxo

      Thank's, that worked well, except it doesn't display the last names alphabetically... I'm not quite sure what the problem is--I think it may have to do something with the keys being numbers, and we are telling it to sort by them. This is my first time using associatve arrays, so bare with me :P...anyway here's the new code:

      #!/usr/bin/perl -w use strict; open(TEST, 'test.txt') || die "Couldn't open it"; print "\nFile Opened\n\n\n"; my $i=1; my %list; while(<TEST>){ chomp; /(\w+)\s+(\w+)/; print "$i : $2\,$1\n"; $list{$i} = [$1,$2]; $i+=1; } foreach my $c (sort keys %list) { print "$list{$c}->[1],$list{$c}->[0]$/"} close(TEST); die "\n\n\nFile Closed\n";

      Input file:

      Emmitt Smith
      Walter Payton
      Barry Sanders
      Jim Brown
      Jeremy Smith
      

      Oh yeah, what do you mean by dereferencing?

      Thanks,
      Emmitt

        Your second take is much better, much more indented, but try and keep things neat. Your closing brace in your foreach, for example, is strangely at the end of a line which is also sans-semicolon.

        Anyway, to "derefence" something is to take a reference and use it to obtain a value. References are what make people exposed to C for the first time look all green and dizzy, but they're really not that bad, especially in Perl.

        Here's a really, really brief introduction to references:
        my @array = qw[ 1 2 3 ]; my $array_ref = \@array; # Backslash makes a reference my @array_copy = @$array_ref; # @ de-references array reference $array[2] = 4; # Modifies @array directly print $array_ref->[2]; # Should be '4' now $array_ref->[1] = 5; # Modifies @array by reference print $array[1]; # Should be '5' $array_copy[1] = 6; # Modifies @array_copy, not @array print $array[1]; # Still '5'
        For a much more detailed document, check out perlref.

        Fortunately, or unfortunately depending on your opinion, Perl will automagically dereference for you in certain circumstances. Where $foo->{bar} is actually an "ARRAY" reference, you should really sub-address that as $foo->{bar}->[1] and not $foo->{bar}[1], but either should work.
Re: Associative Array Trouble
by mt2k (Hermit) on May 23, 2002 at 05:15 UTC
    mmm... might I add one bug that might occur from this example? What happens if the person has a last name containing spaces? I know a couple of people on this planet with last names like that... So I made one small adjustment to your code. I used a split() with a limit of 2:

    #!/usr/bin/perl -w use strict; my $i = 1; my %list; open TEST, 'test.txt' || die "Couldn't open it"; print "\nFile Opened\n\n\n"; while(<TEST>){ chomp; my ($f, $l) = split(' ', $_, 2); print "$i : $l\,$f\n"; $list{$i} = [$f,$l]; $i += 1; } foreach my $c (sort keys %list) { print "$list{$c}->[1],$list{$c}->[0]$/"; } close TEST; print "\n\n\nFile Closed\n";

    Also, may I ask why $list{$c}->[1],$list{$c}->[0] and $list{$c}[1],$list{$c}[0] are not really the same thing?
    Is it because without the dereferencing arrows, it is not "technically correct" code?
    I have never used the arrows and my code has always worked fine!

Re: Associative Array Trouble
by rbc (Curate) on May 22, 2002 at 23:43 UTC
    If you have Unix-like tools you could do ...
    $ cat test.txt | sed -e's/\(.*\) \(.*\)/\2,\1/g' | sort Brown,Jim Payton,Walter Sanders,Barry Smith,Emmitt
        Hmmm ... maybe we can have a golf game?
        What would be shorter a shell script or a perl script?
        My money would be on the shell script. What do you think merlyn?