2

I have an array of key names and need to remove any keys that are not in this list from a hash.

I gather deleting keys in a hash is a Bad Thing while iterating over it, but it does seem to work:

use strict;
use warnings;
use Data::Dumper;

my @array=('item1', 'item3');
my %hash=(item1 => 'test 1', item2 => 'test 2', items3 => 'test 3', item4 => 'test 4');

print(Dumper(\%hash));

foreach (keys %hash)
{
    delete $hash{$_} unless $_ ~~ @array;
}    

print(Dumper(\%hash));

gives the output:

$VAR1 = {
      'item3' => 'test 3',
      'item1' => 'test 1',
      'item2' => 'test 2',
      'item4' => 'test 4'
    };
$VAR1 = {
      'item3' => 'test 3',
      'item1' => 'test 1'
    };

What is a better/cleaner/safer way of doing this?

1 Answer 1

3

Don't use smartmatch ~~, it's fundamentally broken and will likely be removed or substantially changed in upcoming releases of Perl.

The easiest solution is to build a new hash only containing those elements you're interested in:

my %old_hash = (
    item1 => 'test 1',
    item2 => 'test 2',
    item3 => 'test 3',
    item4 => 'test 4',
);
my @keys = qw/item1 item3/;

my %new_hash;
@new_hash{@keys} = @old_hash{@keys};  # this uses a "hash slice"

If you want to update the original hash, then do %old_hash = %new_hash afterwards. If you don't want to use another hash, you might like to use List::MoreUtils qw/zip/:

# Unfortunately, "zip" uses an idiotic "prototype", which we override
# by calling it like "&zip(...)"
%hash = &zip(\@keys, [@hash{@keys}]);

which has the same effect.

Sign up to request clarification or add additional context in comments.

1 Comment

Is there any way to do it without a temporary hash to hold the slice??

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.