按哈希值Perl哈希值哈希值排序

时间:2009-04-17 22:09:29

标签: perl sorting hash perl-data-structures

我有一个类似于以下的哈希结构:

KeyA => {
         Key1 => {
                   Key4 => 4
                   Key5 => 9
                   Key6 => 10
                 }
         Key2 => {
                   Key7 => 5
                   Key8 => 9
                 }
        }
KeyB => {
         Key3 => {
                   Key9 => 6
                   Key10 => 3
                 }
        }

我需要通过哈希结构和遍历末尾的值打印出遍历路径,以便按值排序。 例如,对于上面的哈希结构,我需要打印:

KeyB Key3 Key10 3
KeyA Key1 Key4  4
KeyA Key2 Key7  5
KeyB Key3 Key9  6
KeyA Key2 Key8  9
KeyA Key1 Key5  9
KeyA Key1 Key6  10

目前,为了解决这个问题,我使用嵌套的foreach循环遍历哈希结构,并通过插入一个键等于遍历路径的元素(例如“KeyA Key3 Key10”)创建一个扁平哈希,并且值等于at的值遍历路径的结束(例如3),然后执行另一个foreach循环,按值对整理的散列进行排序。

有更有效的方法吗?

5 个答案:

答案 0 :(得分:3)

考虑创建排序数组,而不是创建新哈希。迭代初始值,根据值,键值对插入到数组中,然后迭代生成的数组。这应该为初始迭代提供O(n)+ O(lg n)为每次插入+ O(n)进行最后一次迭代。

答案 1 :(得分:3)

您还可以使用递归解决方案为任意嵌套深度的嵌套数据结构解决此问题。您递归地构建包含路径和值的目标数组,然后对该数组进行排序。

use warnings;
use strict;

sub paths {
    my ($data, $cur_path, $dest) = @_; 
    if (ref $data eq 'HASH') {
        foreach my $key (keys %$data) {
            paths($data->{$key}, [@$cur_path, $key], $dest);
        }   
    } else {
        push @$dest, [$cur_path, $data];
    }   
}

my $data = {
    KeyA => {
        Key1 => { Key4 => 4, Key5 => 9, Key6 => 10 },
        Key2 => { Key7 => 5, Key8 => 9 }
    },
    KeyB => { Key3 => { Key9 => 6, Key10 => 3 } }
};

my $dest = []; 
paths($data, [], $dest);

foreach my $result (sort { $a->[1] <=> $b->[1] } @$dest) {
    print join(' ', @{$result->[0]}, $result->[1]), "\n";
}

答案 2 :(得分:1)

对于您提供的数据结构,实际上并不是嵌套循环的替代方法。 (可能有更好的数据结构,但我们无法知道。)我会这样编码:

use strict;
use warnings;

my %hash = (
    KeyA => {
        Key1 => {
            Key4 => 4,
            Key5 => 9,
            Key6 => 10,
        },
        Key2 => {
            Key7 => 5,
            Key8 => 9,
        },
    },
    KeyB => {
        Key3 => {
            Key9 => 6,
            Key10 => 3,
        },
    },
);

my @array;
while (my ($k1, $v1) = each %hash) {
    while (my ($k2, $v2) = each %$v1) {
        while (my ($k3, $v3) = each %$v2) {
            push @array, [$k1, $k2, $k3, $v3];
        }
    }
}

foreach my $x (sort { $a->[-1] <=> $b->[-1] } @array) {
    print join(' ', @$x), "\n";
}

答案 3 :(得分:0)

使用多维哈希模拟将其转换为平面哈希(请参阅perlvar中的$;),然后对生成的哈希进行排序。

use strict;
use warnings;
my %hash = (
    KeyA => {
          Key1 => {
                    Key4 => 4,
                    Key5 => 9,
                    Key6 => 10,
                  },
          Key2 => {
                    Key7 => 5,
                    Key8 => 9,
                  }
         },
    KeyB => {
          Key3 => {
                    Key9 => 6,
                    Key10 => 3,
                  },
         },
);

my %fhash = 
   map {
        my @fh;
        foreach my $k2 (keys %{$hash{$_}}) {
                foreach my $k3 (keys %{$hash{$_}{$k2}}) {
                        push @fh, (join($;, $_, $k2, $k3) => $hash{$_}{$k2}{$k3});
                }   
        }
        @fh;
   } keys %hash;



foreach (sort { $fhash{$a} <=> $fhash{$b} } keys %fhash) {
    printf("%s\t%d\n", join("\t", split(/$;/, $_)), $fhash{$_});
}

您可以将生成fhash的map / foreach循环直接传递给排序。

答案 4 :(得分:-2)

这些其他解决方案似乎更优雅,因为它们“聪明”。但是,考虑到数据结构的简单性,您的方法实际上很好。这种结构很容易变平。你问了一个更有效的解决方案,没有提供。