Neater way of checking / populating a hash required

N

niall.macpherson

I have some code which reads a number of key / value pairs. It needs to
store the largest value encountered for each key in the data in a hash.

This example code does what I want but I am sure there must be a better
way of doing it as the if(defined ...) part looks messy to me. If I
remove the if defined(...) check , then of course the line.

if($value > $biggest{$key})

gives a warning

Use of uninitialized value in numeric gt (>) at
C:\develop\NiallPerlScripts\clpm
14.pl line 14, <DATA> line 1.

the first time a new key is encountered.

Can anyone suggest a better way of writing this ?

--------------------------------- Start code
------------------------------------------------------------
use strict;
use warnings;
use Data::Dumper;

my %biggest;

while(<DATA>)
{
chomp;
my($key, $value) = split(/,/);
if(defined ($biggest{$key}))
{
if($value > $biggest{$key})
{
$biggest{$key} = $value;
}
}
else
{
$biggest{$key} = $value;
}
}
print Dumper \%biggest;

__DATA__
1,123
2,234
3,356
2,444
3,666
--------------------------------- End code
 
A

Anno Siegel

bugbear said:
if(!exists($biggest{$key}) || $value > $biggest{$key}) {
$biggest{$key} = $value;
}

Existence of a key doesn't guarantee a defined value. You want

if( !defined( $biggest{ $key}) || ... ) {

Anno
 
U

Uri Guttman

AS> Existence of a key doesn't guarantee a defined value. You want

AS> if( !defined( $biggest{ $key}) || ... ) {

assuming that these values are all > 0 you can drop one hash access with
this:

$biggest{$key} = $value if $value > ( $biggest{$key} || 0 ) ;

who cares if the old value was defined/exists or not. you just want to
see if the new value is > than the old one. the || 0 is to silence the
warning on undef.

uri
 
T

Tad McClellan

Can anyone suggest a better way of writing this ?

if(defined ($biggest{$key}))
{
if($value > $biggest{$key})

if ( defined($biggest{$key}) and $value > $biggest{$key} )
{
 
B

Brad Baxter

I have some code which reads a number of key / value pairs. It needs to
store the largest value encountered for each key in the data in a hash.

Since you're talking hash, I'm probably reading too much into your
sample data. But if your keys *are* all small integers, you could
use an array. (I'm also assuming your values are > 0).

use warnings;
use strict;

my @biggest;

while(<DATA>)
{
chomp;
my( $key, $value ) = split /,/;
$biggest[ $key ] = $value if $value > ( $biggest[ $key ] || 0 );
}

for my $key ( 0 .. $#biggest ) {
$_ and print "$key: $_\n" for $biggest[ $key ];
}

__DATA__
1,123
2,234
3,356
2,444
3,666
 
A

Anno Siegel

Brad Baxter said:
I have some code which reads a number of key / value pairs. It needs to
store the largest value encountered for each key in the data in a hash.

Since you're talking hash, I'm probably reading too much into your
sample data. But if your keys *are* all small integers, you could
use an array. (I'm also assuming your values are > 0).

use warnings;
use strict;

my @biggest;

while(<DATA>)
{
chomp;
my( $key, $value ) = split /,/;
$biggest[ $key ] = $value if $value > ( $biggest[ $key ] || 0 );
}

for my $key ( 0 .. $#biggest ) {
$_ and print "$key: $_\n" for $biggest[ $key ];
}

__DATA__
1,123
2,234
3,356
2,444
3,666

This thread kept me wondering if we have to use an ad-hoc maximum routine
in the presence of a pre-fabricated List::Util::max. So I'm drawing it
in. If it kicks and screams that may be because it amounts to slurping
the entire file before reducing the data to the maxima.

use List::Util qw( max);

my @biggest;
while(<DATA>)
{
chomp;
my( $key, $value ) = split /,/;
push @{ $biggest[ $key] }, $value;
}
$_ = max @$_ for grep defined, @biggest;

for my $key ( 0 .. $#biggest ) {
defined and print "$key: $_\n" for $biggest[ $key ];
}
__DATA__
1,123
2,234
3,356
2,444
3,666

The same technique would apply if a hash %biggest were used.

Anno
 
B

Ben Morrow

Quoth Uri Guttman said:
AS> Existence of a key doesn't guarantee a defined value. You want

AS> if( !defined( $biggest{ $key}) || ... ) {

assuming that these values are all > 0 you can drop one hash access with
this:

$biggest{$key} = $value if $value > ( $biggest{$key} || 0 ) ;

who cares if the old value was defined/exists or not. you just want to
see if the new value is > than the old one. the || 0 is to silence the
warning on undef.

Or just no warnings 'uninitialized' in an appropriately small scope
around it: cleaner IMHO to say what you mean :).

Ben
 
B

Brad Baxter

Ben said:
Or just no warnings 'uninitialized' in an appropriately small scope
around it: cleaner IMHO to say what you mean :).

The code below prints:

$VAR1 = {
'1' => '123',
'3' => 666,
'2' => 444
};

I expected it to print:

$VAR1 = {
'1' => 123,
'3' => 666,
'2' => 444
};

That is, I expected the '>' operator to always give
$value the property of having been used as a number.
But the fact that it's compared only with undef appears
to mean it's not given that property.

This is perl, v5.8.7 built for sun4-solaris

-- Brad

use warnings;
use strict;
use Data::Dumper;

my %biggest;

while(<DATA>)
{
chomp;
my( $key, $value ) = split /,/;
for( $biggest{ $key } ) {
no warnings 'uninitialized';
$_ = $value if $value > $_;
}
}

print Dumper \%biggest;

__DATA__
1,123
2,234
3,356
2,444
3,666
 
B

Ben Morrow

Quoth "Brad Baxter said:
The code below prints:

[ code snipped; it uses > to compare an uninitialized hash elem with a
string containing a number ]
$VAR1 = {
'1' => '123',
'3' => 666,
'2' => 444
};

I expected it to print:

$VAR1 = {
'1' => 123,
'3' => 666,
'2' => 444
};

That is, I expected the '>' operator to always give
$value the property of having been used as a number.
But the fact that it's compared only with undef appears
to mean it's not given that property.

When does this matter? As a rule, in Perl you don't need to worry about
string/number conversions, and just trust perl to do it for you
correctly.

Ben
 
B

Brad Baxter

Ben said:
Quoth "Brad Baxter said:
The code below prints:

[ code snipped; it uses > to compare an uninitialized hash elem with a
string containing a number ]
$VAR1 = {
'1' => '123',
'3' => 666,
'2' => 444
};

I expected it to print:

$VAR1 = {
'1' => 123,
'3' => 666,
'2' => 444
};

That is, I expected the '>' operator to always give
$value the property of having been used as a number.
But the fact that it's compared only with undef appears
to mean it's not given that property.

When does this matter? As a rule, in Perl you don't need to worry about
string/number conversions, and just trust perl to do it for you
correctly.

I can't give a good example of when it might matter. Comparing
Dumper output in tests, maybe? (Not saying that's a good example.)
Mainly, I'm asking for my own edification and on the off chance that
this behavior might be a symptom of a subtle bug.

Apparently, when an operand is a string containing a number, the
only time it is not affected this way by the '>' operator (or '<',
'==', etc.) is when the right hand side is undef. That just doesn't
seem right. Does it matter? I can't say for sure, but it seems
inconsistent.

# use warnings;
use strict;
use Data::Dumper;
$Data::Dumper::Terse++;
$Data::Dumper::Indent=0;

my $x = '1'; my $y;
show();

$y = '0';
show();

$x = '1'; $y = 'a';
show();

$x = undef; $y = '1';
show();

sub show {
my $dummy = $x > $y;
print Dumper [$x, $y];
}

__END__
['1',undef][1,0][1,'a'][undef,1]
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top