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

hi Monks, I need help to sort a hash with keys and values. I am new to perl hashes and would appreciate some help.

I want to sort first by the keys and then values. Here is my hash and what I wrote. I cannot get it sort by values.

$timing1{$related_pin}{$timing_type}{$rise_fall}{$pinname} = $delay; } foreach my $rName ( keys %timing1 ) { foreach my $tType ( sort keys %{$timing1{$rName}}) { foreach my $rF ( sort keys %{$timing1{$rName}{$tType}}){ foreach my $pinName ( keys %{$timing1{$relName}{$tType}{$rF}}) { foreach my $delay(sort {$timing1{$relName}{$timeType}{$rF}{$pinName}{$ +a} <=> $timing1{$rName}{$tType}{$rF}{$pinName}{$b} }keys %timing1){ print OUTFILE2 " $pinName\t $rName\t $tType\t $rF\t $timing1{$rName}{$ +tType}{$rF}{$pinName} \n"; } } } } }

Replies are listed 'Best First'.
Re: Sorting Multilevel Hashes
by Not_a_Number (Prior) on Apr 24, 2014 at 23:00 UTC

    I don't have an answer for you, but, then, you don't have a question, do you? :-)

    I do, however, have several comments:

    • There are several egregious syntax errors in your code (e.g. the unwanted curly at the end of line 1, the use of $relName rather than $rName in line 6...) Always(!) put

      use strict; use warnings;

      at the top of your code to avoid these errors (recommended reading: Use strict warnings and diagnostics or die).
    • Please, please, adopt an Indent style. Perl doesn't care, but it'll make your code far more readable to us humans.
    • In order to help you, we most likely need some indication of what your input data looks like, and of what output you would like from this data.
    • On a point of detail, I note that your outermost hash is keyed on $related_pin. Are you sure that this value will be unique for all of your data?

      I am reading an input file to create a hash. My data looks like this. This is after I ran the code without sorting by the value. I need to add the sort by value,ie delay.

      pin Name related_pin time_type rise_fall delay DQ5_RX_CLK M2CLKP c_rise rise_transition 0.014446 DQ2_RX_CLK M2CLKP c_rise rise_transition 0.014464 DQ0_RX_CLK M2CLKP c_rise rise_transition 0.014452 DQ3_RX_CLK M2CLKP c_rise rise_transition 0.014452 DQ7_RX_CLK M2CLKP c_rise rise_transition 0.014430 DQ4_RX_CLK M2CLKP c_rise rise_transition 0.014446 DQ8_RX_CLK INT_CLK c_fall fall_transition 0.199360 DQ6_RX_CLK INT_CLK c_fall fall_transition 0.199322 DQ1_RX_CLK INT_CLK c_fall fall_transition 0.199500 DQ5_RX_CLK INT_CLK c_fall fall_transition 0.199248 DQ2_RX_CLK INT_CLK c_fall fall_transition 0.199368

      to create the hash I read in a file

          $timing1{$related_pin}{$timing_type}{$rise_fall}{$pinname} = $delay;

      Here is my code

      foreach my $rName ( keys %timing1 ) { foreach my $tType ( sort keys %{$timing1{$rName}}) { + foreach my $rF ( sort keys %{$timing1{$rName}{$tType}}){ + foreach my $pinName ( keys %{$timing1{$rName}{$tType}{$rF}}) +{ print OUTFILE2 " $pinName\t $rName\t $tType\t $rF\t $timin +g1{$rName}{$tType}{$rF}{$pinName} \n"; } } } }
        Something like this :
        use strict; use warnings; my %timing1; chomp ($_=<DATA>); # Hdr my @hdr = split /\s+/; while (<DATA>){ chomp ; s/^\s+//; my @r=split /\s+/; $timing1{$r[1]}{$r[2]}{$r[3]}{$r[0]}=$r[4]; } foreach my $rName ( keys %timing1 ) { foreach my $tType ( sort keys %{$timing1{$rName}}) { foreach my $rF ( sort keys %{$timing1{$rName}{$tType}}){ + my @by_val = map {[$timing1{$rName}{$tType}{$rF}{$_},$_]} keys %{$timing1{$rName}{$tType}{$rF}}; @by_val = sort { $a->[0] <=> $b->[0]} @by_val; foreach my $aref ( @by_val) { my ($val, $pinName) = @$aref; print " $pinName\t $rName\t $tType\t $rF\t $val \n"; } } } } __DATA__ pin Name related_pin time_type rise_fall delay DQ5_RX_CLK M2CLKP c_rise rise_transition 0.014446 DQ2_RX_CLK M2CLKP c_rise rise_transition 0.014464 DQ0_RX_CLK M2CLKP c_rise rise_transition 0.014452 DQ3_RX_CLK M2CLKP c_rise rise_transition 0.014452 DQ7_RX_CLK M2CLKP c_rise rise_transition 0.014430 DQ4_RX_CLK M2CLKP c_rise rise_transition 0.014446 DQ8_RX_CLK INT_CLK c_fall fall_transition 0.199360 DQ6_RX_CLK INT_CLK c_fall fall_transition 0.199322 DQ1_RX_CLK INT_CLK c_fall fall_transition 0.199500 DQ5_RX_CLK INT_CLK c_fall fall_transition 0.199248 DQ2_RX_CLK INT_CLK c_fall fall_transition 0.199368
        OUTPUT:
        DQ7_RX_CLK M2CLKP c_rise rise_transition 0.014430 DQ4_RX_CLK M2CLKP c_rise rise_transition 0.014446 DQ5_RX_CLK M2CLKP c_rise rise_transition 0.014446 DQ3_RX_CLK M2CLKP c_rise rise_transition 0.014452 DQ0_RX_CLK M2CLKP c_rise rise_transition 0.014452 DQ2_RX_CLK M2CLKP c_rise rise_transition 0.014464 DQ5_RX_CLK INT_CLK c_fall fall_transition 0.199 +248 DQ6_RX_CLK INT_CLK c_fall fall_transition 0.199 +322 DQ8_RX_CLK INT_CLK c_fall fall_transition 0.199 +360 DQ2_RX_CLK INT_CLK c_fall fall_transition 0.199 +368 DQ1_RX_CLK INT_CLK c_fall fall_transition 0.199 +500

                What is the sound of Perl? Is it not the sound of a wall that people have stopped banging their heads against?
                      -Larry Wall, 1992

Re: Sorting Multilevel Hashes
by hdb (Monsignor) on Apr 25, 2014 at 06:29 UTC

    If you only want to print the sorted rows into another file, then IMHO it is easier to read the file into an array of arrays and sort them then.

    use strict; use warnings; my $header = <DATA>; my @rows = map { join "\t", @$_ } sort { $a->[1] cmp $b->[1] || $a->[2] cmp $b->[2] || $a->[3] cmp $b->[3] || $a->[4] <=> $b->[4] } map {[ split /\s+/ ]} <DATA>; print $header; print "$_\n" for @rows; __DATA__ pin Name related_pin time_type rise_fall delay DQ5_RX_CLK M2CLKP c_rise rise_transition 0.014446 DQ2_RX_CLK M2CLKP c_rise rise_transition 0.014464 DQ0_RX_CLK M2CLKP c_rise rise_transition 0.014452 DQ3_RX_CLK M2CLKP c_rise rise_transition 0.014452 DQ7_RX_CLK M2CLKP c_rise rise_transition 0.014430 DQ4_RX_CLK M2CLKP c_rise rise_transition 0.014446 DQ8_RX_CLK INT_CLK c_fall fall_transition 0.199360 DQ6_RX_CLK INT_CLK c_fall fall_transition 0.199322 DQ1_RX_CLK INT_CLK c_fall fall_transition 0.199500 DQ5_RX_CLK INT_CLK c_fall fall_transition 0.199248 DQ2_RX_CLK INT_CLK c_fall fall_transition 0.199368
Re: Sorting Multilevel Hashes
by hippo (Archbishop) on Apr 24, 2014 at 22:45 UTC

    To my eyes that code is bordering on unmaintainable. If you are attempting to sort by key at each level in turn (which is what I think you are doing - correct me if that isn't the case) then think about recursion. Applying the relevant FAQ (which also discusses sorting by value) just once in a recursive subroutine should do the trick without resorting to $foo{$bar}{$baz}{$quux}{$frob} constructs.

      thats right . I want to sort by keys first and then value. It works fine when I do not attempt to add a sort by value. If you could provide an example of how to sort by value in this kind of hash I would really appreciate it.

        Here's an example of sorting by key using recursion.

        #!/usr/bin/perl -Tw # # Multi-level hash sort use strict; use warnings; my %timing1 = initmyhash(); printsortedhash (\%timing1, ''); exit; sub initmyhash { # Obviously, put whatever sets up your hash here. return ( dog => 'rover', cat => { name => 'sandy', age => 10 } ); } sub printsortedhash { my ($this, $report) = @_; for my $key (sort keys %$this) { if (ref $this->{$key} eq 'HASH') { printsortedhash ($this->{$key}, $report . "\t $key") } else { print $report . "\t $key\t $this->{$key}\n"; } } }

        The FAQ linked above shows how to sort by value instead/also. Take your pick.

Re: Sorting Multilevel Hashes
by Anonymous Monk on Apr 24, 2014 at 23:26 UTC