catfish1116 has asked for the wisdom of the Perl Monks concerning the following question:
I'm working on this exercise to print out from a hash table. However, when I have an undefined value or a 'blank' value I don't want to print that out. Below is my code:
###################################################
## #
## 6/2/20 #
## Program prints out a report out of birthdays #
## hash table only if name AND birthday exist #
## in hash #
## #
###################################################
my %birthdays = (
Fred => 'June 1',
Ray => 'May 31',
Glenn => 'May 27',
Bob => 'August 28',
Jim => '',
Paul => undef,
);
print"\nName Birthday\n";
print "-" x 25 ."\n";
foreach my $name ( keys %birthdays) {
if (exists $birthdays{$name} ) {
print " $name $birthdays{$name}\n";
}
}
TIA
The catfish
Re: Printing from a hash table
by haukex (Archbishop) on Jun 02, 2020 at 21:29 UTC
|
You can use defined to test for undef and length to test for empty strings, and then either use an if to exclude those values like you're already using, or as an alternative use next to skip the current iteration of the loop.
By the way, keys only returns the keys that already exist in the hash, so your exists check isn't necesary in this case.
| [reply] [d/l] [select] |
Re: Printing from a hash table
by AnomalousMonk (Archbishop) on Jun 02, 2020 at 21:39 UTC
|
foreach my $name ( keys %birthdays) {
if (exists $birthdays{$name} ) {
...
}
}
If you're iterating over the keys of a hash (as in the quoted code above), each key must exist in the hash! One way to do something like what you want:
c:\@Work\Perl\monks>perl -wMstrict -e
"my %birthdays = (
Fred => 'June 1',
Ray => 'May 31',
Glenn => 'May 27',
Bob => 'August 28',
Jim => '',
Paul => undef,
);
;;
my $fmt = ' %-10s %s';
printf qq{$fmt \n}, 'Name', 'Birthday';
print '-' x 25, qq{\n};
;;
B_DAY:
foreach my $name (keys %birthdays) {
next B_DAY
unless defined $birthdays{$name}
and length $birthdays{$name}
;
printf qq{$fmt \n}, $name, $birthdays{$name};
}
"
Name Birthday
-------------------------
Ray May 31
Bob August 28
Glenn May 27
Fred June 1
Give a man a fish: <%-{-{-{-<
| [reply] [d/l] [select] |
Re: Printing from a hash table
by kcott (Archbishop) on Jun 03, 2020 at 06:56 UTC
|
| [reply] [d/l] [select] |
Re: Printing from a hash table
by Tux (Canon) on Jun 03, 2020 at 08:11 UTC
|
As 0 is most likely an invalid/undefined birthday too, you just want "true" values. All good advice already given, the shortest route might be:
printf "%-12s %s\n", $_, $birthdays{$_} for grep { $birthdays{$_} } so
+rt keys %birthdays;
-->
$ perl test.pl
Name Birthday
-------------------------
Bob August 28
Fred June 1
Glenn May 27
Ray May 31
Enjoy, Have FUN! H.Merijn
| [reply] [d/l] [select] |
Re: Printing from a hash table
by BillKSmith (Monsignor) on Jun 03, 2020 at 03:57 UTC
|
When you need both the key and the value of a hash in a loop, the function each is convenient.
use strict;
use warnings;
my %birthdays = (
Fred => 'June 1',
Ray => 'May 31',
Glenn => 'May 27',
Bob => 'August 28',
Jim => '',
Paul => undef,
);
while (my ($name, $B_day) = each %birthdays) {
next if !defined $B_day or $B_day eq '';
printf "%9s : %-9s\n", $name, $B_day;
}
Output:>/
Glenn : May 27
Fred : June 1
Ray : May 31
Bob : August 28
| [reply] [d/l] [select] |
|
Careful, each has a nasty gotcha - iterator state! I personally never use it because of this and having been bit by it a few times.
The iterator used by each is attached to the hash or array, and is shared between all iteration operations applied to the same hash or array. Thus all uses of each on a single hash or array advance the same iterator location. All uses of each are also subject to having the iterator reset by any use of keys or values on the same hash or array, or by the hash (but not array) being referenced in list context. This makes each-based loops quite fragile: it is easy to arrive at such a loop with the iterator already part way through the object, or to accidentally clobber the iterator state during execution of the loop body. It's easy enough to explicitly reset the iterator before starting a loop, but there is no way to insulate the iterator state used by a loop from the iterator state used by anything else that might execute during the loop body. To avoid these problems, use a foreach loop rather than while-each.
| [reply] |
Re: Printing from a hash table
by soonix (Canon) on Jun 03, 2020 at 08:51 UTC
|
- As haukex already stated, exists is superfluous, as keys can give you existing keys only, to loop over
- for this purpose, undef and empty (and zero) are to be handled the same, you can simply test if/unless $birthdays{$name}. See Scalar values in perldata:
A scalar value is interpreted as FALSE in the Boolean sense if it is undefined, the null string or the number 0 (or its string equivalent, "0"), and TRUE if it is anything else. The Boolean context is just a special kind of scalar context where no conversion to a string or a number is ever performed.
(and just as I am composing this, Tux beat me to it :-) too much research and interruptions :-(
| [reply] [d/l] [select] |
Re: Printing from a hash table
by Marshall (Canon) on Jun 03, 2020 at 07:05 UTC
|
use warnings;
use strict;
my %birthdays = (
Fred => 'June 1',
Ray => 'May 31',
Glenn => 'May 27',
Bob => 'August 28',
Jim => '',
Paul => undef,
);
foreach my $name ( sort keys %birthdays)
{
next unless defined $birthdays{$name}; #skip undef
next if $birthdays{$name} eq ''; #skip blank
print "$name $birthdays{$name}\n";
}
__END__
Bob August 28
Fred June 1
Glenn May 27
Ray May 31
| [reply] [d/l] |
|
|