Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can anyone explain why foreach worked but not map

I tried to put key-value pairs at %hash1 if key exists in %hash There is one element in array for which there is no entry at %hash ex: @array = (1,2,3,4,5); #there is no hash entry for key 1 at %hash

So I thought map would do the job and I will get 4 keys in my new hash i.e. %hash1 but it gives 5 keys. At the same time I tried foreach and it worked. I was in delusion that we can replace foreach using map, but this case made me to think. Can anyone explain, where my logic goes wrong?

#Method 1. Comment it while using Method 2
%hash1 = map { $_=>$hash{$_}  if(exists $hash{$_}) } @array;

# Method 2. Comment whole loop while using method 1
foreach (@array){
    $hash1{$_} = $hash{$_} if(exists $hash{$_});
}
like image 376
Jassi Avatar asked Nov 30 '25 05:11

Jassi


2 Answers

Your problem is that your map expression returns undef a false value for the first element in @array. And that gets stringified to an empty string as it's being used as a hash key. (In the comments Borodin points out that this explanation is incorrect. In fact the empty string comes from the false value that is returned from exists when the key is "1")

You might get a better idea of what is doing on if you a) turn on strict and warnings and b) use Data::Dumper to display the hash once you've created it.

#!/usr/bin/perl

use strict;
use warnings;
use 5.010;

use Data::Dumper;

my @array = (1 .. 5);
my %hash = ( 2 => 'two', 3 => 'three', 4 => 'four', 5 => 'five' );

my %hash1 = map { $_=>$hash{$_}  if(exists $hash{$_}) } @array;

say Dumper \%hash1;

That shows that you end up with a hash like this:

$ ./hash 

Odd number of elements in hash assignment at ./hash line 12.
$VAR1 = {
          '' => 2,
          'three' => 4,
          'five' => undef,
          'two' => 3,
          'four' => 5
        };

You are generating a list with an odd number of elements. And that doesn't make a happy hash.

When you're building a hash you need to ensure that you have an even number of elements. So when you're using map you need to return either zero or two elements for each iteration. So you need something like this:

#!/usr/bin/perl

use strict;
use warnings;
use 5.010;

use Data::Dumper;

my @array = (1 .. 5);
my %hash = ( 2 => 'two', 3 => 'three', 4 => 'four', 5 => 'five' );

my %hash1 = map { exists $hash{$_} ? ($_ => $hash{$_}) : () } @array;

say Dumper \%hash1;

Note that we explicitly return an empty list when a key isn't found in the first hash.

$ ./hash2
$VAR1 = {
          '4' => 'four',
          '3' => 'three',
          '2' => 'two',
          '5' => 'five'
        };
like image 194
Dave Cross Avatar answered Dec 02 '25 21:12

Dave Cross


map will always return what you put in its code block. So the return value for

%hash1 = map { $_=>$hash{$_}  if(exists $hash{$_}) } @array;

will be $_=>$hash{$_} when $hash{$_} exists and "" if it doesn't exist.

What you probably wanted to write:

my %hash1 = map { exists($hash{$_}) ? ($_ => $hash{$_}) : () }
like image 39
Sebastian Stumpf Avatar answered Dec 02 '25 21:12

Sebastian Stumpf