2011-08-19 18 views
5

Tôi tin rằng đây là cách bạn thường sẽ sắp xếp một băm theo giá trị:Sorting một hash bởi giá trị khi nó có nhiều phím

foreach my $key (sort { $hash{$a} <=> $hash{$b} } (keys %hash)) { 
    print "$key=>$hash{$key}"; 
} 

này sẽ in ra các giá trị nhỏ nhất đến lớn nhất.

Bây giờ, nếu tôi có một băm như thế này:

$hash{$somekey}{$somekey2}{$thirdkey} 

Làm thế nào để sắp xếp theo giá trị và có thể nhận được tất cả các phím không?

+0

Vì vậy, bạn muốn sắp xếp không quan tâm đến độ sâu trong cấu trúc? – DavidO

+0

Có độ sâu cố định là 3 khóa trong giá trị băm này và tôi muốn sắp xếp giá trị băm theo giá trị của tất cả ba khóa tồn tại. – petranaya

Trả lời

3

Tôi chỉ sẽ tạo ra một hash mới:

my %new; 
for my $k1 (keys %hash) { 
    for my $k2 (keys %{$hash{$k1}}) { 
    for my $k3 (keys %{$hash{$k1}{$k2}}) { 
     $new{$k1,$k2,$k3} = $hash{$k1}{$k2}{$k3}; 
    } 
    } 
} 

my @ordered = sort { $new{$a} <=> $new{$b} } keys %new; 
for my $k (@ordered) { 
    my @keys = split($;, $k); 
    print "key: @k  - value: $new{$k}\n"; 
} 
+0

Cảm ơn, tôi vừa kết thúc bằng cách sử dụng này. – petranaya

+2

Hãy coi chừng rằng '$;' phải là một giá trị * không bao giờ xuất hiện trong các phím * (mặc định là "\ x1c", ký tự điều khiển), nếu không mã này sẽ bị lỗi và ghi. – hobbs

+0

@FMc - cảm ơn! bài đã sửa. – ErikR

1

Đây là một cách để làm điều đó bằng Deep::Hash::Utils.

use Deep::Hash::Utils qw(slurp); 

my %h = (
    A => { 
     Aa => { Aaa => 4, Aab => 5 }, 
     Ab => { Aba => 1 }, 
     Ac => { Aca => 2, Acb => 9, Acc => 0 }, 
    }, 
    B => { 
     Ba => { Baa => 44, Bab => -55 }, 
     Bc => { Bca => 22, Bcb => 99, Bcc => 100 }, 
    }, 
); 

my @all_keys_and_vals = slurp \%h; 
print "@$_\n" for sort { $a->[-1] <=> $b->[-1] } @all_keys_and_vals; 

Output:

B Ba Bab -55 
A Ac Acc 0 
A Ab Aba 1 
A Ac Aca 2 
A Aa Aaa 4 
A Aa Aab 5 
A Ac Acb 9 
B Bc Bca 22 
B Ba Baa 44 
B Bc Bcb 99 
B Bc Bcc 100 
1

Tôi đã làm một cái gì đó tương tự bằng cách di chuyển một tài liệu tham khảo xuống phím băm thích hợp. Sau đó, bạn có thể thực hiện sắp xếp trên con trỏ.

Lợi thế khi thực hiện theo cách này là dễ dàng điều chỉnh nếu mức thay đổi.

Điều tôi đã sử dụng phương pháp này là di chuyển con trỏ đến một mức độ cụ thể một cách có hệ thống bằng cách tham chiếu một dãy các khóa. (Ví dụ: my @Keys = ('Giá trị', 'Giá trị2');)

Tôi tin rằng một dẫn xuất của ví dụ sau có thể cung cấp cho bạn những gì bạn đang tìm kiếm.

my $list_ref; 
my $pointer; 

my %list = (
    Value => { 
     Value2 => { 
     A => '1', 
     C => '3', 
     B => '2', 
     }, 
    }, 
); 

$list_ref = \%list; 
$pointer = $list_ref->{Value}->{Value2}; 

foreach my $key (sort { $pointer->{$a} <=> $pointer->{$b} } (keys %{$pointer})) { 
    print "Key: $key\n"; 
} 
1

Đối với mục đích học tập, đây là một hàm đệ quy khá gọn gàng:

sub flatten_hash { 
    my ($hash, $path) = @_; 
    $path = [] unless defined $path; 

    my @ret; 

    while (my ($key, $value) = each %$hash) { 
    if (ref $value eq 'HASH') { 
     push @ret, flatten_hash($value, [ @$path, $key ]); 
    } else { 
     push @ret, [ [ @$path, $key ], $value ]; 
    } 
    } 

    return @ret; 
} 

mà phải mất một băm như

{ 
    roman => { 
     i => 1, 
     ii => 2, 
     iii => 3, 
    }, 
    english => { 
     one => 1, 
     two => 2, 
     three => 3, 
    }, 
} 

và biến nó thành một danh sách như

(
    [ ['roman','i'], 1 ], 
    [ ['roman', 'ii'], 2 ], 
    [ ['roman', 'iii'], 3 ], 
    [ ['english', 'one'], 1 ], 
    [ ['english', 'two'], 2 ], 
    [ ['english', 'three'], 3 ] 
) 

mặc dù tất nhiên t ông ra lệnh bị ràng buộc thay đổi. Với danh sách đó, bạn có thể sắp xếp nó trên { $a->[1] <=> $b->[1] } hoặc tương tự, sau đó trích xuất đường dẫn khóa từ @{ $entry->[0] } cho mỗi mục nhập. Nó hoạt động bất kể độ sâu của cấu trúc dữ liệu, và ngay cả khi các nút lá không xảy ra tất cả ở cùng độ sâu. Nó cần một chút mở rộng để đối phó với các cấu trúc không hoàn toàn là hashrefs và scalars đơn giản.

Các vấn đề liên quan