2010-05-25 13 views
20

Se ho un hash in Perl che contiene mapping interi completi e sequenziali (cioè, tutte le chiavi da 0 a n sono mappate su qualcosa, nessuna chiave al di fuori di questo), c'è un mezzo per convertirlo in una matrice?Perl, convertire l'hash in array

So che potrei scorrere le coppie chiave/valore e inserirle in un nuovo array, ma qualcosa mi dice che ci dovrebbe essere un mezzo integrato per farlo.

+4

** ATTENZIONE PERICOLO! ** Perché tutti vogliono ordinare le chiavi? Non è necessario e rende l'algoritmo molto più lento! L'ordinamento è lento! La risposta di ** runrig ** è la migliore qui. Funzionerà se l'hash è scarso. Gli array mantengono l'ordine ma sono strutture di accesso casuale. Non stiamo lavorando con liste collegate, gente! – daotoad

+0

Hai ragione: c'è un modo per farlo integrato. Vedi [Risposta di Ether] (http://stackoverflow.com/questions/2907270/perl-convert-hash-to-array/2907469#2907469). :) Questa domanda è un'altra ragione per cui esiste una differenza tra il contesto dell'elenco e gli array.Accoppiato con sezioni, ti permette di fare conversioni tra liste e hash senza particolari magie o iterazioni, e imho è una delle funzionalità più potenti di Perl. –

risposta

20

Se l'origine dati originale è un hash:

# first find the max key value, if you don't already know it: 
use List::Util 'max'; 
my $maxkey = max keys %hash; 

# get all the values, in order 
my @array = @hash{0 .. $maxkey}; 

O se i dati originali fonte è un rifhash:

my $maxkey = max keys %$hashref; 
my @array = @{$hashref}{0 .. $maxkey}; 

Questo è facile da testare con questo esempio:

my %hash; 
@hash{0 .. 9} = ('a' .. 'j'); 

# insert code from above, and then print the result... 
use Data::Dumper; 
print Dumper(\%hash); 
print Dumper(\@array); 

$VAR1 = { 
      '6' => 'g', 
      '3' => 'd', 
      '7' => 'h', 
      '9' => 'j', 
      '2' => 'c', 
      '8' => 'i', 
      '1' => 'b', 
      '4' => 'e', 
      '0' => 'a', 
      '5' => 'f' 
     }; 
$VAR1 = [ 
      'a', 
      'b', 
      'c', 
      'd', 
      'e', 
      'f', 
      'g', 
      'h', 
      'i', 
      'j' 
     ]; 
+0

Nessun operatore '->' per l'hashref? – Zaid

+0

@Zaid: no, non è così che usi le slice negli hashrefs. – Ether

+1

Stupido, si sta facendo il cast come array, motivo per cui l'operatore della freccia non è necessario. – Zaid

12

OK, questo non è molto "integrato" ma funziona. È anche IMHO preferibile a qualsiasi soluzione che coinvolge "ordina" in quanto è più veloce.

map { $array[$_] = $hash{$_} } keys %hash; # Or use foreach instead of map 

In caso contrario, meno efficiente:

my @array = map { $hash{$_} } sort { $a<=>$b } keys %hash; 
+6

o per evitare la mappa nel contesto vuoto: $ array [$ _] = $ hash {$ _} per le chiavi% hash; – runrig

+3

Dovrebbe essere un 'sort {$ a <=> $ b}'. Ricorda che 'sort' è il default dei confronti tra stringhe. – Zaid

2

Questo lascerà le chiavi non definiti nel %hashed_keys come undef:

# if we're being nitpicky about when and how much memory 
# is allocated for the array (for run-time optimization): 
my @keys_arr = (undef) x scalar %hashed_keys; 

@keys_arr[(keys %hashed_keys)] = 
    @hashed_keys{(keys %hashed_keys)}; 

E, se si sta utilizzando riferimenti:

@{$keys_arr}[(keys %{$hashed_keys})] = 
    @{$hashed_keys}{(keys %{$hashed_keys})}; 

O, più pericolosamente, poiché presuppone che ciò che hai detto sia vero (potrebbe non essere sempre vero e infernale; Basta dire!):

@keys_arr = @hashed_keys{(sort {$a <=> $b} keys %hashed_keys)}; 

Ma questo è un po 'oltre il punto. Se all'inizio fossero indicizzati a numeri interi, perché adesso sono in un hash?

+0

Buon uso di fette. Un altro aspetto positivo riguardo alla scelta iniziale della struttura dei dati. Peccato che tu abbia richiamato 'sort', sort è lento (vedi il mio rant sopra) e soggetto a errori e non è desiderabile. – daotoad

+0

@daotoad - La prima soluzione (e raccomandata) non usa 'sort'. Ma sono completamente d'accordo su 'sort'; genera fino a chiamate di funzioni arbitrarie n-quadrate per invocazione [suggerimento: è terribile]. – amphetamachine

12

È possibile estrarre tutti i valori da un hash con la funzione values:

my @vals = values %hash; 

Se li volete in un ordine particolare, allora si può mettere le chiavi nell'ordine desiderato e poi prendere un hash slice da che:

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

Come ha detto DVK, non c'è costruito in modo, ma questo farà il trucco:

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

o questo:

my @array; 

keys %hash; 

while (my ($k, $v) = each %hash) { 
    $array[$k] = $v 
} 

punto di riferimento per vedere che è più veloce, la mia ipotesi sarebbe la seconda.

+0

Beh, il primo sarà O (NlogN) in media, ma fino a O (N^2). Il secondo metodo è semplicemente O (N). Non c'è bisogno di parametri di riferimento. – daotoad

4

Perl non fornisce un built-in per risolvere il problema.

Se si sa che le chiavi coprono un determinato intervallo 0..N, è possibile sfruttare questo fatto:

my $n = keys(%hash) - 1; 
my @keys_and_values = map { $_ => $hash{$_} } 0 .. $n; 
my @just_values  = @hash{0 .. $n}; 
+0

Oppure 'my $ n = $ # {[keys% hash]}' se hai un'avversione per la costante sottostringa ... – Zaid

0

Combinando FM 's e Ether' risposte s permette di evitare la definizione di uno scalare altrimenti inutile:

my @array = @hash{ 0 .. $#{[ keys %hash ]} }; 

La cosa accurata è che a differenza con l'approccio scalar, $# funziona anche nell'improbabile caso in cui l'indice predefinito del primo elemento, $[, sia diverso da zero.

Naturalmente, ciò significherebbe la scrittura qualcosa di stupido e offuscato in questo modo:

my @array = @hash{ $[ .. $#{[ keys %hash ]} }; # Not recommended 

ma poi c'è sempre la remota possibilità che qualcuno ha bisogno da qualche parte (WinCE) ...

0
@a = @h{sort { $a <=> $b } keys %h}; 
1
$Hash_value = 
{ 
'54' => 'abc', 
'55' => 'def', 
'56' => 'test', 
}; 
while (my ($key,$value) = each %{$Hash_value}) 
{ 
print "\n $key > $value"; 
} 
0

Siamo in grado di scrivere un po 'come di seguito:

$j =0; 
while(($a1,$b1)=each(%hash1)){ 
    $arr[$j][0] = $a1; 
    ($arr[$j][1],$arr[$j][2],$arr[$j][3],$arr[$j][4],$arr[$j][5],$arr[$j][6]) = values($b1); 
    $j++; 
} 

$ a1 contiene la chiave e $ b1 contiene i valori Nell'esempio sopra ho Hash della matrice e la matrice contiene 6 elementi.

0

Un modo semplice è quello di fare @array = %hash

Ad esempio,

my %hash = (
    "0" => "zero", 
    "1" => "one", 
    "2" => "two", 
    "3" => "three", 
    "4" => "four", 
    "5" => "five", 
    "6" => "six", 
    "7" => "seven", 
    "8" => "eight", 
    "9" => "nine", 
    "10" => "ten", 
); 

my @array = %hash; 

print "@array"; produrrebbe il seguente output,

3 tre 9 nove 5 cinque 8 otto 2 due 4 quattro 1 uno 10 dieci 7 sette 0 zero 6 sei

Problemi correlati