Take the 2-minute tour ×

Is there a way to get a sub-hash? Do I need to use a hash slice?

For example:

%hash = ( a => 1, b => 2, c => 3 );

I want only

%hash = ( a => 1, b => 2 );
share|improve this question
As there is nothing logically to "slice" on I suspect something to do with 'map'. In an array you'd do it based on an index, but in a hash there is no reliably internal representation of the data in it. You'd need some type of qualifier (regex?) to define what should go into the subhash and what shouldn't. –  Ape-inago Jun 11 '09 at 3:59
BTW, you hash syntax is wrong: use round braces "()" for a plain hash, "{}" for anonymous hash references. –  trendels Jun 11 '09 at 7:35
add comment

6 Answers

up vote 24 down vote accepted

Hash slices return the values associated with a list of keys. To get a hash slice you change the sigil to @ and provide a list of keys (in this case "a" and "b"):

my @items = @hash{"a", "b"};

Often you can use a quote word operator to produce the list:

my @items = @hash{qw/a b/};

You can also assign to a hash slice, so if you want a new hash that contains a subset of another hash you can say

my %new_hash;
@new_hash{qw/a b/} = @hash{qw/a b/};

Many people will use a map instead of hash slices:

my %new_hash = map { $_ => $hash{$_} } qw/a b/;
share|improve this answer
this makes why "map" works w/ hashes a little clearer to me. Thanks –  Ape-inago Jun 11 '09 at 4:15
Larry Wall, brian d foy, and...Chas. Owens...thank you for this very comprehensive explanation! –  bernie Jun 11 '09 at 4:19
You should probably make it more clear that a hash slice just has the values, not key/value pairs. –  ysth Jun 11 '09 at 4:20
@ysth Are you happier now? –  Chas. Owens Jun 11 '09 at 5:37
add comment

You'd probably want to assemble a list of keys you want:

my @keys = qw(a b);

And then use a loop to make the hash:

my %hash_slice;
for(@keys) {
  $hash_slice{$_} = %hash{$_};


my %hash_slice = map { $_ => $hash{$_} } @keys;

(My preference is the second one, but whichever one you like is best.)

share|improve this answer
add comment

FWIW, I use Moose::Autobox here:

my $hash = { a => 1, b => 2, c => 3, d => 4 };
$hash->hslice([qw/a b/]) # { a => 1, b => 2 };

In real life, I use this to extract "username" and "password" from a form submission, and pass that to Catalyst's $c->authenticate (which expects, in my case, a hashref containing the username and password, but nothing else).

share|improve this answer
How is $hash->hslice(qw/a b/) better than @{$hash}{qw/a b/}? Or is this one of those cases where if I have to ask I am too far gone to understand? –  Chas. Owens Jun 11 '09 at 5:39
You should have an answer template so that "I use Moose::" is at the start of every answer. :) –  brian d foy Jun 11 '09 at 12:03
Chas. Owens: that's not what hslice does. @{$hash}{...} returns a list of values; hslice returns a new hash reference with both keys and values. –  jrockway Jun 11 '09 at 23:25
add comment

Too much functional programming leads me to think of zip first.

With List::MoreUtils installed,

use List::MoreUtils qw(zip);

%hash = qw(a 1 b 2 c 3);
@keys = qw(a b);
@values = @hash{@keys};
%hash = zip @keys, @values;

Unfortunately, the prototype of List::MoreUtils's zip inhibits

zip @keys, @hash{@keys};

If you really want to avoid the intermediate variable, you could

zip @keys, @{[@hash{@keys}]};

Or just write your own zip without the problematic prototype. (This doesn't need List::MoreUtils at all.)

sub zip {
    my $max = -1;
    $max < $#$_and $max = $#$_ for @_;
    map { my $ix = $_; map $_->[$ix], @_; } 0..$max;

%hash = zip \@keys, [@hash{@keys}];

If you're going to be mutating in-place,

%keep = map +($_ => 1), qw(a b);
$keep{$a} or delete $hash{$a} while ($a, $b) = each %hash;

avoids the extra copying that the map and zip solutions incur. (Yes, mutating the hash while you're iterating over it is safe... as long as the mutation is only deleting the most recently iterated pair.)

share|improve this answer
Explain this, please -- %keep = map +($_ => 1), qw(a b); ? –  Ilya Matveychikov Dec 19 '13 at 9:59
add comment

Yet another way:

my @keys = qw(a b);
my %hash_copy;
@hash_copy{@keys} = @hash{@keys};
share|improve this answer
add comment

A hash is an unordered container, but the term slice only really makes sense in terms of an ordered container. Maybe look into using an array. Otherwise, you may just have to remove all of the elements that you don't want to produce your 'sub-hash'.

share|improve this answer
add comment

Your Answer


