perlhashvariable-assignmenthashref

Hashref assignment consumes next key instead of assigning undef


I'm trying to initialize a hashref containing the results of several expressions. I would expect expressions that return undefined results to assign undef to the appropriate key. Instead, the assignment just gobbles up the next key as though the expression was never there to begin with.

A quick example is probably easier to understand:

use Data::Dumper;
my $str = "vs";
my $contains = {
    t => ($str =~ /t/i),
    u => ($str =~ /u/i),
    v => ($str =~ /v/i),
};
print(Data::Dumper->Dump([$contains]));

I would expect the code above to print:

$VAR1 = {
    'v' => 1,
    't' => undef,
    'u' => undef
};

Instead, I get the following:

$VAR1 = {
    't' => 'u',
    'v' => 1
};

Adding an explicit undef to the assignment does get me the result I'm looking for:

use Data::Dumper;
my $str = "vs";
my $contains = {
    t => ($str =~ /t/i || undef),
    u => ($str =~ /u/i || undef),
    v => ($str =~ /v/i || undef),
};
print(Data::Dumper->Dump([$contains]));

However, this seems a little counterintuitive to me. Can anybody explain this behavior?


Solution

  • You're running into the problem of list context vs scalar context. To get your desired output, you must force the match to be in scalar context, e.g. like scalar($str =~ /t/i), or ($str =~ /t/i) || undef if you really want undef in case the match fails.

    A regex match /.../ behaves differently in different contexts:

    Here, you have no capture groups so your regex matches evaluate to the empty list if they don't match – not undef. So the hashref construct actually sees these values:

    my $contains = {
        't',
        'u',
        'v', 1,
    };
    

    The fat-arrow => operator does not create key–value pairs, it is just a variant of the comma operator that turns the key on the left side into a string.