简体   繁体   English

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

[英]Sort by value hash of hash of hashes Perl

I have a hash structure similar to the following: 我有一个类似于以下的哈希结构:

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

I need to print out the traversal path through the hash structure and the value at the end of the traversal, such that this is ordered by value. 我需要通过哈希结构打印遍历路径和遍历结束时的值,以便按值排序。 For example, for the above hash structure I need to print: 例如,对于上面的哈希结构,我需要打印:

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

Currently, to solve this I am traversing the hash structure using nested foreach loops, and creating a flattened hash by inserting an element with key equal to the traversal path (eg "KeyA Key3 Key10") and value equal to the value at the end of the traversal path (eg 3), then doing another foreach loop which sorts the flattened hash by value. 目前,为了解决这个问题,我使用嵌套的foreach循环遍历哈希结构,并通过插入具有等于遍历路径的键的元素(例如“KeyA Key3 Key10”)来创建扁平哈希,并且值等于末尾的值。遍历路径(例如3),然后执行另一个foreach循环,按值对扁平散列进行排序。

Is there a more efficient way to do this? 有没有更有效的方法来做到这一点?

Instead of creating a new hash, consider creating a sorted array. 考虑创建排序数组,而不是创建新哈希。 Iterate over the initial values, inserting in to the array, according to the value, the key-value pair, then iterate over the resulting array. 迭代初始值,根据值,键值对插入到数组中,然后迭代生成的数组。 This should give you O(n) on the initial iteration + O(lg n) for each insertion + O(n) for the final iteration. 这应该为初始迭代提供O(n)+ O(lg n)为每次插入+ O(n)进行最后一次迭代。

You can also solve this problem for a nested data structure of arbitrary nesting depth using a recursive solution. 您还可以使用递归解决方案为任意嵌套深度的嵌套数据结构解决此问题。 You recursively build up a destination array containing paths and values, and then sort that array. 您递归地构建包含路径和值的目标数组,然后对该数组进行排序。

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";
}

For the data structure you've given there's not really an alternative to nested looping. 对于你给出的数据结构,实际上并不是嵌套循环的替代方法。 (There might be a better data structure, but there's no way for us to know.) I'd code it this way: (可能有更好的数据结构,但我们无法知道。)我这样编码:

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";
}

Convert it into a flat hash using multidimensional hash emulation (see $; in perlvar), then sort the resulting hash. 使用多维哈希模拟将其转换为平面哈希(请参阅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{$_});
}

You could pass the the map / foreach loop that generates fhash directly to the sort. 您可以将生成fhash的map / foreach循环直接传递给排序。

These other solutions are seemingly more elegant because they're "clever". 这些其他解决方案似乎更优雅,因为它们“聪明”。 However, given the simplicity of your data structure, your method is actually just fine. 但是,考虑到数据结构的简单性,您的方法实际上很好。 That structure is easily flattened. 这种结构很容易变平。 You asked for a more efficient solution, none was provided. 你问了一个更有效的解决方案,没有提供。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM