in reply to sort hash elements...

i'm not sure of your requirements. you say you need unique keys... is that because there may be two "keith"s? what if they each get a 90? in your implementation, they're still not unique. or is it because rob and john might both get a 52? if this is a hash value, then there isn't a uniqueness problem.

if the name alone is good for uniqueness, you can use this simple short method, which works with the data you provided.

#!/usr/bin/perl -w use strict; $|++; my @names_marks = ( 'cliff 76', 'john 52', 'keith 90', 'rob 52' ); my %hash = map { split } @names_marks; my @sorted_names = sort { $hash{$b} <=> $hash{$a} } keys %hash; print $hash{$_},' ',$_,$/ for @sorted_names;
also, your code
foreach (@names_marks) { ($names[$_], $marks[$_]) = split / /, $_; $hash{"$marks[$_]".$names[$_]} = ucfirst($names[$_]). " " . $marks[$ +_]; }
is not correct. $_ contains the value from @names_marks, not the index. so you're trying to access $names[keith], which isn't numeric. you cannot ignore these warnings, your code is broken.

anytime you think 'unique', think hash. i don't think you need arrays at all, but you might know something i don't.

~Particle ;Þ

Replies are listed 'Best First'.
Re: Re: sort hash elements...
by kiat (Vicar) on Mar 27, 2002 at 15:06 UTC
    Hi Particle,

    Thanks! I tried out your code and got it works perfectly. Thanks for pointing '$_' out - I got confused with 'for' and 'foreach' and thought it was an index.

    I don't understand the following line in your code and would appreciate it very much if you could explain how it works:

    my %hash = map { split } @names_marks;
    Another question I've is "What if I've two persons with the same name?". When I added another 'john' to the data, only one of them shows up. How do I avoid this problem?

      first, i'll explain my code:
      my %hash = map { split } @names_marks;
      okay, let's break it down.

       @names_marks is a list of items, containing strings with two items seperated by a single space, such as 'keith 90'. you want to seperate these items.

      split, by default, will split on spaces, and will split $_. it's in the documentation: online at split, or command line at perldoc -f split. so split could be replaced with split ' ', $_

      i can be sure that split will return a list, since the doc (for perl 5.6.1) says scalar context is deprecated. since i know the data only contains one space, an item on either side, i can use these two items to assign to a hash.

      it's perfectly legal to say

      my %hash = ( 'bob', 1 ); # or my %hash = ( bob => 1 );
      where i'm assigning the first list element as a key in %hash, and the second list element as the key's value.

      on a sidenote--you can use Data::Dumper; to see into data structures. add <use Data::Dumper;</code> to the top of the script, and print Dumper [%hash]; after the hash is created. you'll see the keys and values. this is particularly helpful for debugging complex data structures.

      map is somewhat similar to for or foreach, when used to iterate over a list, see the doc: map. (by the way, for and foreach are the same, docs: for, foreach.)

      i could turn my map into a for:

      my %hash; for( @names_marks ) { %hash = split } # or, expanding split for( @names_marks ) { %hash = split ' ', $_ }
      note there's no semi-colon between the braces--this is okay because the final statement in braces does not require a trailing semi-colon.

      okay, i hope that helps you a bit. now, on to the problem of two persons with the same name. my questions to you are: how do *you* know who's who? if you're reporting who got what marks, and there are two johns, wouldn't each want to know which mark he received?

      in this case, you'll need unique names, like 'john1', or 'john q. public'. the former will work with my current implementation. the latter will require some coding changes. for instance, space is no longer a good seperator for @names_marks, because the names might have spaces in them. instead, use a pipe, or colon, or carat, or question mark, or exclamation point. make sure it's some character that won't show up in either data field.

      then, split the data on the seperator instead of space. hash keys can have spaces in them, so the rest of my example should still work. although, if this were important code, i'd recommend a more robust implementation than the one i've provided.

      ~Particle ;Þ

        Hi Particle,

        Many thanks for taking time to explain:)

        I now understand what "my %hash = map { split } @names_marks;" does - splits the array of elements into keys and values - each array element contains a name and a score delimited by space. The 'map' can be replaced by a 'for' loop to achieve the same result.

        With regard to name uniqueness, the program that I'm testing will need to parse a large number of entries (names of pupils for a particular schooling level and their scores for a certain subject). I want to be able to deal with situations where there is more than one person with the same name. My approach was to append a person's name to her score to make the name unique. But as you've pointed out, if the two persons have the same score, the name (key) will still not be unique. I'm still thinking how I could get around that problem. Any advice will be appreciated :)

        cheers,

        kiat