Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"

undef-safe equality

by hubb0r (Pilgrim)
on Apr 21, 2005 at 19:24 UTC ( #450166=perlquestion: print w/replies, xml ) Need Help??
hubb0r has asked for the wisdom of the Perl Monks concerning the following question:

I've been doing a lot of database work, and I end up doing a lot of comarisons between my returned dataset and other things. The problem I'm encountering is that if I pull back a NULL value in the dataset, and do a string comparison against it, perl throws warnings about "Use of uninitialized value in string comparison" (paraphrased).

Now, I understand that these are only warnings, but I like to run with -w, and I also have a logfile that stdout and stderr are piped to, and I HATE seeing errors in it!!

I thought that in one of the PERL6 snippets I've seen that there is an operator like what I am talking about, but I don't know of a clean way to do it in PERL5. I find myself writing this:
if ($ret->{'val1'} eq $this->{'val1'}) { ### }

Which throws the warnings when $ret->{'val1'} is undef. So I end up going back and cluttering up my if statement like:
if ($ret->{'val1'} and $ret->{'val1'} eq $this->{'val1'} { ### }

This makes the warnings go away, but definitely looks more cluttered and can become downright unruly depending on the if statement (and depending on which values might be undef).

Anyone have any better suggestions?

Replies are listed 'Best First'.
Re: undef-safe equality
by dragonchild (Archbishop) on Apr 21, 2005 at 19:30 UTC
    Write your own function. (This one is adapated from Test::Builder, the basis for Test::More's is() test.)
    sub is_eq { my ($l, $r) = @_; if (defined $l && defined $r) { return $l eq $r; } else { return !defined $l && !defined $r; } }

    Updated: Fixed typo in transcription from Test::Builder::is_eq()

      Your second return should be:
      return !defined $l && !defined $r;

      Otherwise, two undefs will compare unequal.

Re: undef-safe equality
by tlm (Prior) on Apr 21, 2005 at 19:50 UTC

    In this case, I think dragonchild's solution is the way to go, but in general, you can switch warnings off locally and selectively; e.g.:

    { no warnings 'uninitialized'; if ( $ret->{'val1'} eq $this->{'val1'} ) { # ... } }

    the lowliest monk

Re: undef-safe equality (shorter)
by tye (Sage) on Apr 21, 2005 at 20:04 UTC

    Here's a shorter version (compared to the others I saw posted):

    sub iseq { return defined($_[0]) == defined($_[1]) && $_[0] eq $_[1]; }

    Update: Drat. You can't get away with that shortcut and still avoid the warning.

    - tye        

      But you can do
      sub iseq { (!defined $_[0] || $_[0].9) eq (!defined $_[1] || $_[1].9); }
      Update - shorter:
      1 == keys %{{map {(!defined()||$_.9)=>1} @_}};

      Caution: Contents may have been coded under pressure.
Re: undef-safe equality
by ysth (Canon) on Apr 22, 2005 at 05:22 UTC
    Is there some value like "" that you want a NULL to be considered equal to? If so,
    if ( ($ret->{val1}||"") eq ($this->{val1}||"") ) ...
    if ( defined($ret->{val1}) == defined($this->{val1}) && ($ret->{val1}||"") eq ($this->{val1}||"") ) ...
Re: undef-safe equality
by donarb (Beadle) on Apr 23, 2005 at 17:52 UTC
    One other option not mentioned, but some databases allow you to modify the returned NULL value to some standard string value, like "(null)". Of course, you have to choose a value that will never, ever be used in your data.

    Postgres, for example uses the 'pset' command to modify printed values. You should be able to pass pset values in the options parameter of DBI->connect. Note that the following is not tested, but I think should work based in DBI and Postgres documentation. If you cannot modify the printed NULL value through DBI, you may be able to modify it in the database startup configuration.

    my $options = "--pset null='(null)'"; my $dbh = DBI->connect("dbi:Pg:dbname=test;" . "options=$options", "user", "password");
Re: undef-safe equality
by hubb0r (Pilgrim) on Apr 22, 2005 at 18:58 UTC
    Thanks for all the responses. I like the idea of having my own sub that does the checking and returns a reasonable answer. That (I think) will definitely make for a cleaner representation of what the code is actually doing.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://450166]
Approved by Paladin
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (4)
As of 2018-10-22 21:04 GMT
Find Nodes?
    Voting Booth?
    When I need money for a bigger acquisition, I usually ...

    Results (122 votes). Check out past polls.