tags:

views:

62

answers:

2

I am building a hash where the keys, associated with scalars, are not necessarily unique. The desired behavior to be that if the key is unique, the value is the scalar. If the key is not unique, I want the value to be an array reference of the scalars associated witht the key. Since the hash is built up iteratively, I don't know if the key is unique ahead of time. Right now, I am doing something like this:

if(!defined($hash{$key})){
   $hash{$key} = $val;
}
elseif(ref($hash{$key}) ne 'ARRAY'){
   my @a;
   push(@a, $hash{$key});
   push(@, $val);
   $hash{$key} = \@a;
}
else{
   push(@{$hash{$key}}, $val);
}

Is there a simpler way to do this?

+1  A: 
if(!defined($hash{$key})){
    $hash{$key} = $val;
}
elsif (ref($hash{$key}) ne 'ARRAY') {
    $hash{$key} = [ $hash{$key}, $val ];
}
else{
   push(@{$hash{$key}}, $val);
}
Uh Clem
You should really use `exists` in your first conditional -- there's a chance that `undef` might show up in `$hash{$key}`, in which case your final condition would clobber it.
friedo
+5  A: 

rjh is right on the money.

I have written way too much code that does exactly what you describe--the hash value is an array ref unless it isn't. Reams and reams of conditional type checking. Then one day it hit me, "Why am I writing all this crap? Just use an array ref everywhere, dummy" I said to myself. Since that day bluebirds fly down from the trees to sing to me whenever I walk in the park.

push @{$hash{$key}}, $val;

That's all you have to do. If the key does not exist, the array is autovivified.

If you don't like autoviv, and want to be explicit do:

$hash{$key} = [] unless exists $hash{$key};
push @{$hash{$key}}, $val;

Even this 'verbose' approach is much shorter.

daotoad
Am I the only one to immediately imagine the scene from *Shrek* where the bluebird and Fiona sing and the bird explodes? :)
DVK
Wow, I didn't know that even an arrayref dereference would be autovivified. So I no longer have to do `@{ $hash{$key} ||= [] }` ... neat!
rjh