develooper Front page | perl.beginners | Postings from August 2009

Re: Inverting a hash safely

Thread Previous | Thread Next
From:
Jenda Krynicky
Date:
August 3, 2009 15:20
Subject:
Re: Inverting a hash safely
Message ID:
4A777E8D.13167.15D9BECE@Jenda.Krynicky.cz
From: Ed Avis <eda@waniasset.com>
> Jenda Krynicky <Jenda <at> Krynicky.cz> writes:
> 
> >>     my %hash = (a => 1, b => 2);
> >>     my %reverse = safe_hash_invert %hash; # works fine
> >>
> >>     $hash{c} = 1;
> >>     %reverse = safe_hash_invert %hash; # throws an error 
> 
> >I don't think there is and I don't think there's a need.
> >
> >my %hash = (a => 1, b => 2,
> > c => 1,
> >);
> >my %reverse = reverse %hash;
> >die "Bummer, the values were not unique!"
> > if keys(%hash) != keys(%reverse);
> 
> Of course this works.  But even at three lines of code it's still worth making
> into a function.  The 'any' and 'none' functions in List::MoreUtils are even
> more trivial but it's still very handy to have them.
> 
> To give a really useful error message is a bit more code:
> 
>     my %reverse;
>     foreach my $k (sort keys %hash) {
>         my $v = $hash{$k};
>         if (exists $reverse{$k}) {
>             die "cannot reverse: $v is mapped to by both $k and $reverse{$k}\n";
>         }
>         $reverse{$k} = $v;
>     }

And is this message useful? I mean, if you as the user of the program 
receive this message does it tell you anything useful? Will you know 
what hash was it? Maybe you can find out because the values remind 
you of something you saw for example in a config file, but maybe not.

The function would only be of some use if you could specify the 
message. Otherwise you'd have to wrap it in an eval{}, catch it, make 
sure it's this message and rethrow the exception with a more 
informative text. And hope the next version of the module doesn't 
change the message.

> However, even if it's just one extra line of code as you suggest, that still
> makes it temptingly easy to just forget the check.  I did so myself, and while
> I am no Don Knuth, I'm more conscientious than some people I know!  So if I
> can forget to check it so can many others.

You can't prevent people from shooting themselves into their feet.

> So one reason to have a function providing this is to give a simple FAQ entry
> on reversing a hash: 'just use Whatever::Module::safe_hash_invert'.  And, for
> those who like that kind of thing, a simple way to audit existing code for
> bugs (or latent bugs) caused by hash reversing without checking; I would ideally
> provide both safe_hash_invert and unsafe_hash_invert so that the programmer can
> be explicit about what's intended.

If anything, it would be better to provide a function that'd check 
whether the values of the hash are unique so that you could
  assert(values_are_unique \%hash);
before the reverse(). Because you should only reverse hashes that by 
definition have unique values. So this is something you should test, 
but do not necessarily want to test each and every time in the 
release build.

And if you feel like it, create a function that reverses

 (a => 1, b => 3, c => 1) => (1 => ['a','c'], 2 => ['b'])

That's something that's not a SIMPLE oneliner. Even though of course 
it's not too complex either.

Jenda
===== Jenda@Krynicky.cz === http://Jenda.Krynicky.cz =====
When it comes to wine, women and song, wizards are allowed 
to get drunk and croon as much as they like.
	-- Terry Pratchett in Sourcery


Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About