23
votes

If I have a hash in Perl that contains complete and sequential integer mappings (ie, all keys from from 0 to n are mapped to something, no keys outside of this), is there a means of converting this to an Array?

I know I could iterate over the key/value pairs and place them into a new array, but something tells me there should be a built-in means of doing this.

11
RANT WARNING! Why is everyone wanting to sort the keys? There is no need to and it makes the algorithm so much slower! Sorting is slow! runrig's answer is the best one here. It will work if the hash is sparse. Arrays maintain order but they are random access structures. We aren't working with linked lists, people!daotoad
You're right: there is a way to do it built in. See Ether's answer. :) This question is another reason why there's a difference between list context and arrays. Coupled with slices, it lets you do conversions between lists and hashes without any special magic or iteration, and imho is one of the most powerful features of Perl.Robert P

11 Answers

22
votes

If your original data source is a 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};

Or if your original data source is a hashref:

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

This is easy to test using this example:

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'
        ];
21
votes

You can extract all the values from a hash with the values function:

my @vals = values %hash;

If you want them in a particular order, then you can put the keys in the desired order and then take a hash slice from that:

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

OK, this is not very "built in" but works. It's also IMHO preferrable to any solution involving "sort" as it's faster.

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

Otherwise, less efficient:

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

Perl does not provide a built-in to solve your problem.

If you know that the keys cover a particular range 0..N, you can leverage that fact:

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

This will leave keys not defined in %hashed_keys as 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)};

And, if you're using references:

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

Or, more dangerously, as it assumes what you said is true (it may not always be true … Just sayin'!):

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

But this is sort of beside the point. If they were integer-indexed to begin with, why are they in a hash now?

1
votes
$Hash_value = 
{
'54' => 'abc',
'55' => 'def',
'56' => 'test',
};
while (my ($key,$value) = each %{$Hash_value})
{
 print "\n $key > $value";
}
0
votes

As DVK said, there is no built in way, but this will do the trick:

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

or this:

my @array;

keys %hash;

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

benchmark to see which is faster, my guess would be the second.

0
votes
@a = @h{sort { $a <=> $b } keys %h};
0
votes

Combining FM's and Ether's answers allows one to avoid defining an otherwise unnecessary scalar:

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

The neat thing is that unlike with the scalar approach, $# works above even in the unlikely event that the default index of the first element, $[, is non-zero.

Of course, that would mean writing something silly and obfuscated like so:

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

But then there is always the remote chance that someone needs it somewhere (wince)...

0
votes

We can write a while as below:

$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 contains the key and $b1 contains the values In the above example i have Hash of array and the array contains 6 elements.

0
votes

An easy way is to do @array = %hash

For example,

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"; would produce the following output,

3 three 9 nine 5 five 8 eight 2 two 4 four 1 one 10 ten 7 seven 0 zero 6 six