Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chriss Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Sorting keys in a nested hash

Status
Not open for further replies.

RottPaws

Programmer
Mar 1, 2002
478
US
I'm trying to sort the keys in a nested hash and it's not working right.

My data is sort of like this
Code:
%status_info -- group | member | snapshot | condition
    
AAA |
    |- 1 |
         |- 1 = Good      
         |- 2 = Good 
         |- 3 = Bad  
         |- 4 = Good     
    |- 2 |
         |- 1 = Good      
         |- 2 = Ugly 
         |- 3 = Good  
         |- 4 = Good  
    |- 3 |
         |- 1 = Good      
         |- 2 = Good 
         |- 3 = Good  
         |- 4 = Good  
ABA .....

I'm looping through each group and trying to sort the member numbers to display the status information in a report.

I originally had:
Code:
foreach $this_mem (sort keys %{$status_info{$this_tg}}){
  .... deleted stuff ....
}

which got me the following as expected
Code:
1
10
11
12
.....

So I checked some books and searched the web and came up with these:

Code:
foreach $this_mem (sort {$rpt_info{$this_tg}{$a} cmp $rpt_info{$this_tg}{$b}}
             keys %{$rpt_info{$this_tg}}) {

and 

foreach $this_mem (sort {$rpt_info{$this_tg}{$a} <=> $rpt_info{$this_tg}{$b}}
             keys %{$rpt_info{$this_tg}}) {

These sort of worked most of the time, but I got patches like
Code:
70
71
132
133
134
74
75

and 

130
131
262
263
264
265
266
267
268
135
136

Any help will be greatly appreciated.


_________
Rott Paws

...It's not a bug. It's an undocumented feature!!!
 
it really does not matter if the hash is a nested hash (or multi-dimensional hash). You just need to use the proper sorting commands:

'cmp' sorts alpha/ASCII
'<=>' sorts numerically

'cmp' is the default sort in the abscence of '<=>' or modifiers to 'cmp' like 'uc' or 'lc' or a regexp, etc. You can sort on mixed data using || (or) operator:

sort {$a <=> $b || $a cmp $b} @data;

- Kevin, perl coder unexceptional!
 
I'm still stumped. The member number is numeric. It comes from an Oracle field with datatype NUMBER(5).

I am using the following:
Code:
foreach $this_mem (sort {$status_info{$this_tg}{$a} <=> $status_info{$this_tg}{$b}}
                        keys %{$status_info{$this_tg}}) {

The results are mostly sorted by member number, but at roughly doubling increments, beginning around 36, higher members are jammed in where they don't belong:
Code:
This group has 840 members.
35
[COLOR=red]72[/color]
36

71
[COLOR=red]131
132
133
134[/color]
73

130
[COLOR=red]261
262
263
264
265
266
267[/color]
135

260
[COLOR=red]517
518
519
520
521
522
523
524
525
526
527
528
529
530
531[/color]
268

516
532

Am I doing something wrong?

_________
Rott Paws

...It's not a bug. It's an undocumented feature!!!
 
you're sorting the hash keys by the values. But what is it that you are printing in the output? The hash keys or the values of the keys?

- Kevin, perl coder unexceptional!
 
What I listed are the keys at the member level of the nesting.

If I understand what you're saying, I'm pulling the member keys, but they are being sorted based on the snapshot values inside them. Is that correct?

I am trying to get the member keys sorted by numeric value. How do I do that?

_________
Rott Paws

...It's not a bug. It's an undocumented feature!!!
 
You do have the keys sorted by numeric vlaue it looks like by the code you posted. What do the key/value pairs look like?

- Kevin, perl coder unexceptional!
 
They look like what I outlined in the first post. There are 37 groups I'm looking at which are the main keys of the hash.

Each group, in turn, has hash with about 200 - 800 keys which are the member numbers. These are what I'm trying to sort.

Inside each of the member numbers is another hash with 24 keys numeric keys (0-23) which have the values of the condition of the member at the time of the snapshot that hour.

_________
Rott Paws

...It's not a bug. It's an undocumented feature!!!
 
I'm not too good with theoretical data. I need to see real data.

But looking at the list you posted:

35
72
36

71
131
132
133
134
73

130
261
262
263
264
265
266
267
135

260
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
268

516
532

there is some type of double sort going on here, all the numbers that appear out of place in the sort, are themselves sorted if taken out of the main list:

35
36
71
73
130
135
260
268
516
532

I think you are printing too many variables when you do the sort and are seeing keys from one part of the hash and keys from another part of the hash. Does that make sense to you? Post more of your code.

- Kevin, perl coder unexceptional!
 
Kevin,

Here's the block where I'm having the problem. I'm trying to put the data into a spreadsheet that has the trunk group name in column A (0 in the code), the member number is B (1), and the snapshot values in C-Y (2-25).
Code:
[COLOR=green]#Loop through trunk groups
foreach $this_tg (keys %status_info){

[COLOR=green]    #print "$this_tg\n";
    #Loop through the members[/color]
     foreach $this_mem (sort {$status_info{$this_tg}{$a} <=> $status_info{$this_tg}{$b}}
                            keys %{$status_info{$this_tg}}) {
[COLOR=green]        #print "  -- $this_mem\n";
        #Increment the row number[/color]
        $row_num++;

        $worksheet->write($row_num, 0, $this_tg);
        $worksheet->write($row_num, 1, $this_mem);


[COLOR=green]        #Reset the column number[/color]
        $col_num = 2;

[COLOR=green]        #Loop through the hours and add the trunk states to the spreadsheet[/color]
        foreach $this_hour (@hours) {
[COLOR=green]            #Get the state for this member for the current hour[/color]
            $this_state = $status_info{$this_tg}{$this_mem}{$this_hour};

            if ($this_state =~ "CPB|IDL") {
                $worksheet->write($row_num, $col_num, $this_state);
            }
            else {
                $worksheet->write($row_num, $col_num, $this_state, $red_format);
            }

[COLOR=green]            #Increment the column number[/color]
            $col_num++;

        }

    }[COLOR=green] #End of member loop[/color]

[COLOR=green]    # End loop for testing
    #last;

    #Skip a line before the next trunk group[/color]
    $row_num++;

}[COLOR=green] #End of trunk group loop[/color]

I probably wasn't clear in my problem examples above, but members 1-35 were listed in the proper order, then 72 was thrown in. 36-71 were then in the proper order and then 131-134 were where 72 should have been. 73-130 were good, and so on.

After looking through all the data, I found some groups appeared to be in a totally random order:
Code:
282
322
168
74
7
6
411
264
235
186
107
215
... and so on for 432 members.

_________
Rott Paws

...It's not a bug. It's an undocumented feature!!!
 
Should maybe this first loop also be sorted?

foreach $this_tg (keys %status_info){

- Kevin, perl coder unexceptional!
 
It probably should be for convenience purposes of the people using the report. But I don't see how that would/should affect the sorting of the keys in the sub-hash. I added sort to that line also and it had no effect.

I'm stumped.

_________
Rott Paws

...It's not a bug. It's an undocumented feature!!!
 
I'm stumped too. I can't see the problem in the code you posted. Is that errant data being generated by this line?

Code:
            else {
                $worksheet->write($row_num, $col_num, $this_state, $red_format);

- Kevin, perl coder unexceptional!
 
No. All that logical branch does is turn the text red if the member is "bad" on that particular snapshot.

Even if I comment out the "foreach $this_hour (@hours) {" loop, the member numbers come up out of order.


_________
Rott Paws

...It's not a bug. It's an undocumented feature!!!
 
Can you run the script and uncomment the print lines in the above code? That way you can at least tell which loop the errant numbers are being generated in.

- Kevin, perl coder unexceptional!
 
I had tried that already and it was pulling the group members in the wrong order when it was supposed to be sorting them.

I figured out a way to get what I need. When the numbers come from the database query, I convert them to 3-character strings with leading zeros if they are less than 100. Then at the members loop, I just do a generic sort:
Code:
foreach $this_mem (sort keys %{$rpt_info{$this_tg}}){

I know this isn't the "right way" to do it, but this method works, I don't understand why it wasn't working the other way, and I'm tired of messing with it.

Thanks for all your time trying to help with this. It helps just to know I'm not the only one baffled by this.

_________
Rott Paws

...It's not a bug. It's an undocumented feature!!!
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top