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

I have a file like this (outfile):
mac client;16
windows client;32
my code is
my %calhash; my $key = ""; my $value = ""; while (<OUTFILE>) { my ($key, $value) = split(/;/, $_); chomp; $calhash{$key} = $key; } foreach $key (sort {$b cmp $a} keys %calhash) { print TESTFILE "$calhash{$key};$value\n"; }
I have more code above and below but it works fine so I'm not
worried about it. But I do need help on this section
I think this code takes the file and seperates the two fields
and places them into $key and $value. From there I state
the key field is $key. Then I sort it in reverse order
by the windows or mac client, but my output to TESTFILE
is not right. I have been struggling with this for some time.
I am new and getting frustrated. What am I doing wrong?
Desired output:
windows client;32
mac client;16
Any help is appreciated. Thanks,
Ben

Replies are listed 'Best First'.
Re: Hash sorting
by dmmiller2k (Chaplain) on Mar 13, 2002 at 18:39 UTC

    Good question. Hashes are a source of vague confusion to many programmers (some of whom I've worked with, particularly C++ programmers, treat them with rather a reverence bordering on mysticism).

    First, rearrange your lines to put the chomp BEFORE any code that references $_. This way the value returned from split(), which is being assigned to $value won't end in a newline ("\n").

    Then, assign the VALUE (in $value) to the hash indexed by $key. Your code associates $key with itself, not with $value.

    Also, note that split /;/; is IDENTICAL to what you have, split(/;/, $_); but is more succinct (once you're accustomed to Perl idioms).

    while (<OUTFILE>) { chomp; # <== move the chomp here my ($key, $value) = split /;/; $calhash{$key} = $value; # not '= $key' }

    That addresses the hash loading issues.

    Your printing code is fine, except that you are printing each value ($calhash{$key}) with a blank (see my $value = ""; at the top of your code), not each key and its associated value, as you clearly mean to do:

    foreach my $key (sort {$b cmp $a} keys %calhash) { my $value = $calhash{$key}; print TESTFILE "$key;$value\n"; }

    Update: Apologies to all and sundry who beat me to the punch here. When I started, there were no replies!

    dmm

    If you GIVE a man a fish you feed him for a day
    But,
    TEACH him to fish and you feed him for a lifetime
Re: Hash sorting
by buckaduck (Chaplain) on Mar 13, 2002 at 18:20 UTC
    You're handling the hash wrong. Try this:
    my %calhash; my $key = ""; my $value = ""; while (<OUTFILE>) { chomp; my ($key, $value) = split(/;/, $_); $calhash{$key} = $value; } foreach $key (sort {$b cmp $a} keys %calhash) { print TESTFILE "$key;$calhash{$key}\n"; }

    Update: Dunno what sorting algorithm he wants. I assumed reverse alphanumeric like he typed. I didn't have enough information to change the sorting logic...

    buckaduck

      Good call but doesn't he want to sort by the number of clients and not the client name?

      foreach $key ( sort{ $calhash{$b} <=> $calhash{$a} } keys %calhash ) {

      -derby

      Yahoo! 100th post.

      my %calhash; my $key = ""; my $value = ""; while (<OUTFILE>) { chomp; my ($key, $value) = split(/;/, $_); $calhash{$key} = $value; } foreach $key (sort {$b cmp $a} keys %calhash) { print TESTFILE "$key;$calhash{$key}\n"; }
      Thank you!!!! That worked!!! Why was the chomp placed before the split?
      On this line $calhash{$key} = $value what is that saying?
      Just trying to learn why I was off and my logic for it. Once again, thank you!
      Ben
        (1) the chomp was placed before the split so that the value being split didn't have a newline. With the chomp afterwards, you're removing in the newline from $_, not $value (which was populated before the chomp)
        (2) in your orginal code the statement read "put the value of $key into hash at key '$key'", rather than as the updated code has it. "put the value of $value into the hash at key '$key'.

        rdfield

        1. The chomp comes before the split so that you remove carriage-returns from each line before you split it into $key and $value. If you don't do this, each $value will retain a trailing carriage return. Chomping the line after the split is pointless in this case, since you don't use the implicit variable $_ again.

        2. $calhash{$key} is the value in %calhash which the key $key points to. I'm assigning it a new value $value. Later, if I want the key and value again (like in the printing loop), I'll refer to $key and $calhash{$key}.

        The syntax for Variable Names is covered in perldata of the perl documentation.

        buckaduck

Re: Hash sorting
by broquaint (Abbot) on Mar 13, 2002 at 18:19 UTC
    You need to print $key and the value associated with that key in %calhash.
    print TESTFILE "$key;$calhash{$key}\n";
    Also, $value will always be empty at that point as it has gone out of scope. If you find this sort of thing trips you up often, check out Dominus' comprehensive Coping with Scoping article.
    HTH

    broquaint

Re: Hash sorting
by tomhukins (Curate) on Mar 13, 2002 at 18:31 UTC

    Firstly, there's lots of useful code dealing with this already on the monastery. Type sorting hash into the search box at the top of the page to find it.

    The first potential problem is that your hash values can't contain the ; character. See perlfunc:split (specifically, its LIMIT argument) to find out why. Basically, unless you specify the number of fields split should return, it will create any array using every ; it finds as a delimeter.

    Also, you're assigning the key to be the value at $callhash{$key} = $key. Are you sure you don't want $callhash{$key} = $value instead?

    Finally, think about how scoping works. At the top of your code, you're declaring $key and $value to be empty strings. Then within your while loop you declare new variables, but they are only valid within the scope of that loop. So, when you try to print $value within your foreach loop, you're outputting the empty string you declared at the top of the code. Here, you probably want to remove $value.

    Also, I assume you're using strict and warnings (or -w) earlier on in your code. Good luck!

    Update: I need to learn to type faster. There weren't any replies when I started writing this!

Re: Hash sorting
by dragonchild (Archbishop) on Mar 13, 2002 at 18:24 UTC
    In other words, you would've found this problem immediately by using strict because it would've complained that $value wasn't in scope. :-)

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

Re: Hash sorting
by PrimeLord (Pilgrim) on Mar 13, 2002 at 18:21 UTC
    Well it looks like your print statement is just going to print the values. Printing $hash{$key} will print the keys value. Try changing the statement to:

    print TESTFILE "$key; $calhash{$key}\n";

    And see if that gives you your desired output.

    Update: I was beat to the punch twice. :)