tags:

views:

138

answers:

4

It seems like I should be able to do this with map, but the actual details elude me.

I have a list of strings in an array, and either zero or one of them may have a hash value.

So instead of doing:

foreach $str ( @strings ) {
  $val = $hash{$str} if $hash{$str};
}

Can this be replaced with a one-liner using map?

+4  A: 

Sure, it'd be:

map { $val = $hash{$_} } @strings;

That is, each value of @strings is set in $_ in turn (instead of $str as in your foreach).

Of course, this doesn't do much, since you're not doing anything with the value of $val in your loop, and we aren't capturing the list returned by map.

If you're just trying to generate a list of values, that'd be:

 @values = map { $hash{$_} } @strings;

But it's more concise to use a hash slice:

 @values = @hash{@strings};

EDIT: As pointed out in the comments, if it's possible that @strings contains values that aren't keys in your hash, then @values will get undefs in those positions. If that's not what you want, see Hynek's answer for a solution.

Adam Bellaire
+1 but just use the hash slice and be done with it.
Sinan Ünür
Hash slice will make array with undefs where string is not presented in hash.
Hynek -Pichi- Vychodil
@Hynek: Good point, +1 on your answer which deals with that situation.
Adam Bellaire
+1  A: 
map { defined $hash{$_} && ( $val =  $hash{$_})} @strings;
aartist
+5  A: 
@values = grep { $_ } @hash{@strings};

to account for the fact that you only want true values.

Change this to

@values = grep { defined } @hash{@strings};

if you want to skip undefined values.

Sinan Ünür
+3  A: 

I'm used to do it in this way:

@values = map { exists $hash{$_} ? $hash{$_} : () } @strings;

but I don't see anything wrong in this way

push @values, $hash{$_} for grep exists $hash{$_}, @strings;

or

@values = @hash{grep exists $hash{$_}, @strings};
Hynek -Pichi- Vychodil
+1 However, keep in mind that `@values` may still contain `undef`s. Whether that's desirable depends on the context.
Sinan Ünür