2013-05-23 4 views
2

similaire à cette question: sort by subset of perl stringPerl: trier un hachage par valeur, par clé

Je voudrais d'abord trier par valeur, puis par sous-ensemble de la clé.

mon hachage%

cat_02 => 0 
cat_04 => 1 
cat_03 => 0 
cat_01 => 3 

la sortie: (pourrait être un tableau des clés dans cet ordre)

cat_02 => 0 
cat_03 => 0 
cat_04 => 1 
cat_01 => 3 

Bonus: la comparaison secondaire clé reconnaîtrait 1234_2_C01 et étant plus petite que 1234_34_C01 (cmp ne fonctionne pas)

+1

'2_' est inférieur à' 34' numériquement et alphabétiquement donc c'est un mauvais exemple. Mais je sais ce que tu voulais dire.mis à jour ma réponse –

Répondre

7
my %hash = (
    cat_02 => 0, 
    cat_04 => 1, 
    cat_03 => 0, 
    cat_01 => 3 
); 

print "$_ => $hash{$_}\n" 
    for sort { $hash{$a} <=> $hash{$b} or $a cmp $b } keys %hash; 

Le tri fait la comparaison numérique des valeurs, et si elles ' re égal, la partie après le or est exécutée, ce qui fait la comparaison de chaîne des clés. Cela donne la sortie que vous avez demandée.

Pour le tri intelligent des chaînes qui contiennent des nombres mélangés avec des choses non-numérique, saisir la fonction de comparaison de Alphanum de http://www.davekoelle.com/alphanum.html et remplacer $a cmp $b avec alphanum($a,$b)

+0

quel est l'effet de cmp sur les chiffres dans les chaînes vs les lettres, disons au lieu de chat que vous aviez comme clés 1234_2_C01_HT1 et 1234_23_C01_HT2. – Jabda

+0

très apprécié! – Jabda

+1

@Jabda, 'cmp' se compare dans l'ordre lexicographique, c'est-à-dire' 12 cmp 2' produira '-1', tandis que' 12 <=> 2' produira '1'. Dans un cas particulier, si le nombre de chiffres dans les deux nombres est le même 'cmp' et' <=> 'produira les mêmes résultats. – n0rd

3

Lorsque vous avez une préférence de tri secondaire, vous ajoutez simplement un autre niveau à l'intérieur du sorte de routine:

my %hash = (
    cat_02 => 0, 
    cat_04 => 1, 
    cat_03 => 0, 
    cat_01 => 3 
); 

my @sorted = sort { $hash{$a} <=> $hash{$b} || $a cmp $b } keys %hash; 
        # primary sort method ^^ secondary sort method 
for (@sorted) { 
    print "$_\t=> $hash{$_}\n"; 
} 

sortie:

cat_02 => 0 
cat_03 => 0 
cat_04 => 1 
cat_01 => 3 
+1

Il n'y a pas de "niveaux", de "méthodes de tri primaires" ou de "méthodes de tri secondaires". Il s'agit simplement d'une expression booléenne qui compare les valeurs en premier et si elles sont égales. Je veux dire, l'explication actuelle ressemble à "c'est une syntaxe magique pour trier par' x' puis par 'y'". – n0rd

+0

Bien sûr, il y a des méthodes de tri, et l'une est primaire, l'autre est secondaire, d'un point de vue prioritaire. Ce n'est pas magique, non, comme c'est clairement visible dans le code, il suffit d'utiliser l'opérateur OR logique. – TLP

1

Cela peut ne pas valoir la peine dans ce cas particulier, mais la technique Schwartzian transform peut aussi être utilisée avec le tri multi-critères. Comme cela (codepad):

use warnings; 
use strict; 

my %myhash = (
    cat_2 => 0, cat_04 => 1, 
    cat_03 => 0, dog_02 => 3, 
    cat_10 => 0, cat_01 => 3, 
); 

my @sorted = 
    map { [$_->[0], $myhash{$_->[0]}] } 
    sort { $a->[1] <=> $b->[1] or $a->[2] <=> $b->[2] } 
    map { m/([0-9]+)$/ && [$_, $myhash{$_}, $1] } 
    keys %myhash; 

print $_->[0] . ' => ' . $_->[1] . "\n" for @sorted; 

De toute évidence, la clé de cette technique est d'utiliser plus d'un élément supplémentaire dans le cache. Deux choses ici: 1) @sorted devient en fait un tableau de tableaux (chaque élément de ce tableau est une paire clé-valeur); 2) le tri dans cet exemple est basé sur le suffixe des chiffres des touches (avec une comparaison numérique, pas une chaîne), mais il peut être ajusté dans n'importe quelle direction si nécessaire.

Par exemple, lorsque les touches modèle de correspondance XXX_DD_XXX (et il est DD qui doit être comparé), changer la deuxième map clause avec ceci:

map { m/_([0-9]+)_/ && [$_, $myhash{$_}, $1] } 
4

Cela peut facilement être fait (rapidement!) En utilisant le Tri: : Key :: modules:

use Sort::Key::Natural qw(); 
use Sort::Key::Maker intnatkeysort => qw(integer natural); 

my @sorted_keys = intnatkeysort { $hash{$_}, $_ } keys(%hash); 

Ou vous pouvez profiter des propriétés de vos données et il suffit d'utiliser une sorte naturelle:

use Sort::Key::Natural qw(natkeysort); 

my @sorted_keys = natkeysort { "$hash{$_}-$_" } keys(%hash);