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

I have file containing information about different locations. The location names are the unique values. Some cities will have more than one location.

How do I sort the data by city and then by name and still return all the names?

Right now I have the following code:

while($line = <DATA>){ chomp $line; ($junk,$o_country,$o_province,$o_tcenter,$o_addr +ess,$o_city,$o_stprov,$o_mailcountry,$o_postalcode,$o_firstname,$o_la +stname,$o_title,$o_phone,$o_fax,$o_date) = split(/\|/,$line); #the hashes are filled using the test center as the key $testcenters{$o_city} = $o_tcenter; $centercity{$o_tcenter} = $o_city; ... (more values stored) @mycenters = sort byString keys %testcenters; #returns list of centerc +ity in alpha order foreach $city(@mycenters){ foreach $center($testcenters{$city}){ print "$testcenters{$city}<BR>"; #print testcenter name ... (print more stuff here) } }

I know that is wrong because if there are duplicate cities, then only one location name is returned and the other is skipped.

I've stared at this off and on for, honestly several years, and I the issue came up again today and I realized it was time to ask and hope it was some just obvious solution.

Original node content and title restored by GrandFather


I learn more and more about less and less until eventually I know everything about nothing.

Replies are listed 'Best First'.
Re: How to sort a non-unique value within unique values
by Old_Gray_Bear (Bishop) on Sep 13, 2006 at 21:28 UTC
    It sounds like you want a Hash of Hashes, that looks something like:
    $VAR => { City-Center1=> { Data-items for first combination } City-Center2=> { Data-items for second combination } ... }
    Where the top-level is keyed by the concatenation of the city and center names and a reference to the hash of the data for each city/center combination is the top-level value. Then your center-within-city becomes a simple sort of the top-level keys, and a dereference from the coresponding value.

    In Pseudo-Code (because I haven't run it through the interpreter) --

    my %city_center_hash; # Loading the data structure -- while <DATA> { ($variables) = split data line my $this_key = "$o_city|$o_tcenter"; # select an # appropriate # separater $city_center_hash{$this_key} = { data_item1 => variable1, data_item2 => variable2, # ... data_itemN => last_variable, }; } # .... # .... # Retrieve the data from the data-structure foreach $cc_combo (sort keys %city_center_hash) { my ($this_city, $this_center) = split /\|/, $cc_combo; my $these_data = $city_center_hash{$cc_combo}; my $variable_item1 = $these_data -> {data_item1}; # Do something with the names and the first # data variable # etc .... }

    ----
    I Go Back to Sleep, Now.

    OGB

Re: How to sort a non-unique value within unique values
by tilly (Archbishop) on Sep 14, 2006 at 00:23 UTC
    Use a hash of arrays. When populating them:

    push @{$testcenters{$o_city}, $o_tcenter;
    Then to sort them:
    for my $city (sort keys %testcenters) { for my $center (sort @{$testcenters{$city}}) { print "$city: $testcenters<br>"; } }
    (I could make lots of comments about your variable names, using strict.pm, the way you've embedded HTML in your code, etc, etc, etc. But I'd wind up writing a book, so I'll just suggest reading Perl Best Practices and Code Complete instead.)
Re: How to sort a non-unique value within unique values
by duff (Parson) on Sep 14, 2006 at 03:19 UTC

    I'm not even sure you need the %testcenters hash (at least not for sorting purposes) because it sounds like you want to sort the test centers by city and for that all you need is the %centercity hash that you've already got. From your code it looks like you've got a series of "parallel hashes" all keyed on the test center name which is your unique identifier. You don't need to change this structure to use something more complicated (like the other fine suggestions given so far) if you don't want to.

    Assuming my pile of assumptions are correct, you want something like this:

    my @test_centers_sorted_by_city_then_name = sort { $centercity{$a} cmp + $centercity{$b} || $a cmp $b } keys %centercity; for my $center (@test_centers_sorted_by_city_then_name) { print "$center<br>"; # output test center name print "$centercity{$center}<br>"; # output city where test center +is located # ... output other stuff too }
      Again, my apologies for a very poorly formed question, but this is exactly the solution I was looking for. And I actually understand it, which is a bonus. Its what I'd been trying to figure out for quite awhile. Many thanks!

      I learn more and more about less and less until eventually I know everything about nothing.
Re: How to sort a non-unique value within unique values
by GrandFather (Saint) on Sep 13, 2006 at 20:41 UTC

    Why not give us three lines of data that show the problem, and while you are at it collapse down to the minimum number of fields to show the issue - probably 2.

    You don't show us byString.

    You really, really should add use strict; use warnings; to every script you write or maintain.

    Currently because your variable list is so long and doesn't contain any spaces you node renders about three screen widths wide. It is never good practice to create lines that are longer than can be viewed without scrolling, and good practice generally calls for lines that are fewer than 80 characters wide.


    DWIM is Perl's answer to Gödel
Re: How to sort a non-unique value within unique values
by BrowserUk (Patriarch) on Sep 13, 2006 at 20:34 UTC

    A few lines of example data would be more useful.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.