2009-04-13 9 views
5

Voglio ordinare un hash che in realtà ha un hash come valore. Per esempio:Come posso ordinare un hash di hash per chiave in Perl?

my %hash1=(
    field1=>"", 
    field2=>"", 
    count=>0, 
); 
my %hash2; 
$hash2{"asd"}={%hash1}; 

e ho inserito un sacco di hash per %hash2 con diversi valori di conteggio di %hash2.

Come ordinare lo %hash1 in base al valore di conteggio hash1?

C'è un modo per farlo senza implementare quicksort manualmente, ad esempio con la funzione di ordinamento di Perl?

+0

Vuoi dire che si vuole ottenere l'elenco di hash (come hash1) scelte per il conteggio dei valori in hash2? – Jagmal

+0

sì Jagmal significa che voglio ordinare rispetto a $ hash2 {"asd"} {count}. – systemsfault

risposta

10
my @hash1s = sort {$a->{count} <=> $b->{count}} values %hash2; 
+0

Ho provato in questo modo ma ottenere il seguente avviso: [avviso]: utilizzo del valore non inizializzato nel confronto numerico (<=>) a – systemsfault

+0

Il frammento di codice sia per la mia risposta, sia per il mio commento alla risposta, funziona per me quando testato (anche con -w e "usa rigorosamente"). –

+0

si riceve questo avviso perché uno o più hash non ha valore per il tasto 'count'. Se vuoi contare quelli come 0, puoi ordinare {{$ a -> {count} || 0) <=> ($ b -> {count} || 0)} valori% hash2; – nohat

1

Se si desidera ottenere la lista di hash (come hash1) scelte per il conteggio dei valori in hash2, questo può aiutare:

@sorted_hash1_list = sort sort_hash_by_count_key($a, $b) (values (%hash2); 


# This method can have any logic you want 
sub sort_hash_by_count_key { 
    my ($a, $b) = @_; 
    return $a->{count} <=> $b->{count}; 
} 
+0

Intendi dire "ordina \ & ordina_hash_by_count_key, valori% hash2" invece di ciò che hai attualmente? –

+0

Suppongo che funzionerà anche in questo modo, non è così? – Jagmal

0

Vedi http://perldoc.perl.org/functions/sort.html per il lotto di contesto come funziona ordinamento in Perl.

Ed ecco un esempio .. cercando di essere leggibile, non perlish.

#!/usr/bin/perl 
# Sort Hash of Hashes by sub-hash's element count. 
use warnings; 
use strict; 


my $hash= { 
      A=>{C=>"D",0=>"r",T=>"q"} 
      ,B=>{} 
      ,C=>{E=>"F",G=>"H"} 
      }; 

sub compareHashKeys {0+(keys %{$hash->{$a}}) <=> 0+(keys %{$hash->{$b}}) } 

my @SortedKeys = sort compareHashKeys keys %{$hash}; 
print join ("," , @SortedKeys) ."\n"; 
+0

per cosa è "0+"? – systemsfault

+0

Lo 0+ dovrebbe forzare il valore su un valore numerico, tuttavia lo <=> lo fa già, quindi lo 0+ è davvero ridondante. :-P –

+0

Bene, il valore che esce da cmp o <=> è già numerico, essendo -1, 0 o 1. Non importa quale sia il dato. sort() ha bisogno di -1, 0 o 1 per decidere come ordinare le cose. –

6

Da perlfaq4, la risposta a "http://faq.perl.org/perlfaq4.html#How_do_I_sort_a_hash" ha la maggior parte delle informazioni necessarie per mettere insieme il vostro codice.

Si potrebbe anche voler vedere il capitolo su Ordinamento in Learning Perl.

Chris ha una risposta completa, anche se odio usare values in questo modo. Un modo più familiare per fare la stessa cosa è di passare attraverso le chiavi di hash di livello superiore, ma ordina per la chiave di secondo livello:

my @sorted_hashes = 
    sort { $hash2->{$a}{count} <=> $hash2->{$b}{count} } 
    keys %hash2; 

lo faccio in questo modo perché è un po 'meno mind-bending .


Come si ordina un hash (facoltativamente in base al valore anziché al tasto)?

(contributo di brian d foy)

Per ordinare un hash, iniziamo con le chiavi. In questo esempio, forniamo l'elenco delle chiavi alla funzione di ordinamento che quindi le confronta in modo ASCII (che potrebbe essere influenzato dalle impostazioni locali). L'elenco di output ha le chiavi in ​​ordine ASCIIbetic. Una volta che abbiamo le chiavi, possiamo esaminarle per creare un rapporto che elenca le chiavi in ​​ordine ASCIIbetico.

my @keys = sort { $a cmp $b } keys %hash; 

foreach my $key (@keys) 
    { 
    printf "%-20s %6d\n", $key, $hash{$key}; 
    } 

Tuttavia, nel blocco sort() si potrebbe ottenere di più. Invece di confrontare le chiavi, possiamo calcolare un valore con esse e usare quel valore come confronto.

Ad esempio, per rendere il nostro rapporto senza distinzione tra maiuscole e minuscole, si utilizza la sequenza \ L in una stringa con doppia virgoletta per rendere tutto in minuscolo. Il blocco sort() confronta quindi i valori minuscoli per determinare in quale ordine inserire le chiavi.

my @keys = sort { "\L$a" cmp "\L$b" } keys %hash; 

Nota: se il calcolo è costoso o l'hash ha molti elementi, si consiglia di guardare il Trasformata di Schwartz di memorizzare nella cache i risultati del calcolo.

Se invece vogliamo ordinare per valore hash, usiamo il tasto hash per cercarlo. Abbiamo ancora una lista di chiavi, ma questa volta sono ordinate in base al loro valore.

my @keys = sort { $hash{$a} <=> $hash{$b} } keys %hash; 

Da lì possiamo diventare più complessi. Se i valori hash sono gli stessi, possiamo fornire un ordinamento secondario sulla chiave hash.

my @keys = sort { 
    $hash{$a} <=> $hash{$b} 
     or 
    "\L$a" cmp "\L$b" 
    } keys %hash; 
+0

wow ottima spiegazione grazie. – systemsfault

0

Per ordinare in base all'uso numerico < => e per uso stringa cmp.

# sort by the numeric count field on inner hash 
# 
foreach my $key (sort {$hash2{$a}->{'count'} <=> $hash2{$b}->{'count'}} keys %hash2) { 
    print $key,$hash2{$key}->{'count'},"\n"; 
} 

# sort by the string field1 (or field2) on the inner hash 
# 
foreach my $key (sort {$hash2{$a}->{'field1'} cmp $hash2{$b}->{'field1'}} keys %hash2) { 
    print $key,$hash2{$key}->{'field1'},"\n"; 
} 

Per invertire ordine semplicemente scambiare $ ae $ b:

# sort by the numeric count field on inner hash 
# 
foreach my $key (sort {$hash2{$a}->{'count'} <=> $hash2{$b}->{'count'}} keys %hash2) { 
    print $key,$hash2{$key}->{'count'},"\n"; 
} 

# sort by the string field1 (or field2) on the inner hash 
# 
foreach my $key (sort {$hash2{$a}->{'field1'} cmp $hash2{$b}->{'field1'}} keys %hash2) { 
    print $key,$hash2{$key}->{'field1'},"\n"; 
}