Re: How to test for empty hash?
by haukex (Archbishop) on Aug 05, 2021 at 21:12 UTC
|
The node Truth and Falsehood also briefly discusses this. In short, a hash in boolean context returns a false value when it is empty, so if (!%hash) will portably detect an empty hash. To portably get the number of keys in a hash, use keys in scalar context; scalar(%hash) is not backwards compatible for counting keys (as the documentation quoted by LanX explains).
Minor edits clarification.
| [reply] [d/l] [select] |
|
This is a bit confusingly written.
The question was about checking if a hash is empty, and %h in scalar context can be used to do this portably (though the relative recent change makes it more efficient).
That said,
%h in scalar context can't be used to get the number of keys portably.
keys(%h) in scalar context can be used to get the number of keys portably and efficiently.
That's not true. You could aways use There was a relatively recent change thatefficiency of hash in boolean context
Seeking work! You can reach me at ikegami@adaelis.com
| [reply] [d/l] [select] |
|
This is a bit confusingly written. That's not true. You could aways use There was a relatively recent change thatefficiency of hash in boolean context
| [reply] |
|
update: it's wrong, only read further to learn from mistakes ;-)
> to portably get the number of keys in a hash, use keys in scalar context; scalar(%hash) is not backwards compatible
nitpick for the sake of fun, b/c of the magic of the string_to_number conversion using it as a number will portably work.
(tho I'd also prefer using keys here :)
DB<1> p $]
5.024001
DB<2> %h=(a=>0)
DB<3> p scalar %h
1/8
DB<4> p 0+ %h
1
DB<5>
| [reply] [d/l] [select] |
|
nitpick for the sake of fun, b/c of the magic of the string_to_number conversion using it as a number will portably work.
Sorry, no, it doesn't:
$ perlbrew exec perl -e '%h="a".."z";warn"$] ".keys%h," ".%h," ",0+%h,
+"\n"' >/dev/null
5.034000 13 13 13
5.032001 13 13 13
5.030003 13 13 13
5.028003 13 13 13
5.026003 13 13 13
5.024004 13 8/16 8
5.022004 13 10/16 10
5.020003 13 7/16 7
5.018004 13 11/16 11
5.016003 13 9/16 9
5.014004 13 9/16 9
5.012005 13 9/16 9
5.010001 13 9/16 9
5.010000 13 9/16 9
5.008009 13 9/16 9
5.008001 13 12/16 12
5.006002 13 4/8 4
Minor edits to shorten output. | [reply] [d/l] |
|
Re: How to test for empty hash?
by jdporter (Paladin) on Aug 05, 2021 at 19:55 UTC
|
You're on the right track, but the way to test for the array (of keys) being empty is:
if ( @a == 0 )
You can skip the intermediary variable:
if ( keys(%hash) == 0 )
| [reply] [d/l] [select] |
Re: How to test for empty hash?
by eyepopslikeamosquito (Archbishop) on Aug 06, 2021 at 00:29 UTC
|
| [reply] |
Re: How to test for empty hash?
by haj (Vicar) on Aug 05, 2021 at 19:55 UTC
|
my %hash = (); # empty hash
print "empty!" unless (%hash);
However, this does print -1:
my @a = keys(%hash)
print "$#a\n";
So maybe your hash isn't actually empty? | [reply] [d/l] [select] |
|
> This works for me:
And it's well documented defined in perldata (tho hard to find)
The exact value is only version dependent if the hash is not empty, but still guaranteed to be true (unless tied to magic behaviour)
Maybe not relevant for the OP but IMHO still of interest to people used to the old behaviour.
If you evaluate a hash in scalar context, it returns a false value if the hash is empty. If there are any key/value pairs, it returns a true value. A more precise definition is version dependent.
Prior to Perl 5.25 the value returned was a string consisting of the number of used buckets and the number of allocated buckets, separated by a slash. This is pretty much useful only to find out whether Perl's internal hashing algorithm is performing poorly on your data set. For example, you stick 10,000 things in a hash, but evaluating %HASH in scalar context reveals "1/16", which means only one out of sixteen buckets has been touched, and presumably contains all 10,000 of your items. This isn't supposed to happen.
As of Perl 5.25 the return was changed to be the count of keys in the hash. If you need access to the old behavior you can use Hash::Util::bucket_ratio() instead.
If a tied hash is evaluated in scalar context, the SCALAR method is called (with a fallback to FIRSTKEY).
> So maybe your hash isn't actually empty?
Or tied?
| [reply] |
|
| [reply] [d/l] |
Re: How to test for empty hash?
by GrandFather (Saint) on Aug 06, 2021 at 01:18 UTC
|
Interesting. I have this thing about not doing 0 == blah because I think that reads better as !blah so I wouldn't have had the problem. In many C++ish languages the two are essentially the same so it's pretty much personal preference between an ugly verbose numeric compare and a clean succinct boolean test. In Perl, as you may have found, it can be more subtle than that!
That said, how were you performing your empty test? The simple version works fine for me:
use strict;
use warnings;
my %hash;
print "$^V\n";
print "Empty: !\%hash\n" if !%hash;
print "Empty: ! scalar\n" if ! scalar %hash;
print "Empty: 0 == scalar\n" if 0 == scalar %hash;
print "Empty: ! keys\n" if ! keys %hash;
++$hash{entry};
print "Not empty: \%hash\n" if %hash;
print "Not empty: scalar\n" if scalar %hash;
print "Not empty: 0 != scalar\n" if 0 != scalar %hash;
print "Not empty: keys\n" if keys %hash;
Prints:
v5.32.1
Empty: !%hash
Empty: ! scalar
Empty: 0 == scalar
Empty: ! keys
Not empty: %hash
Not empty: scalar
Not empty: 0 != scalar
Not empty: keys
Update: fixed mismatch between test and print for 0 == scalar
Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
| [reply] [d/l] [select] |
|
Ah hah! This example clarifies my thinking a great deal. As I started this exercise writing a regression test, I kept having trouble inside ok() and is(). (This was using the Test2 suite, but here I will just use Test::Simple.)
#!/usr/bin/perl
use Test::Simple (tests => 9);
use strict;
use warnings;
my %hash;
&ok($^V eq "v5.32.1","Version = v5.32.1");
&ok(!%hash,"Empty: !\%hash");
&ok(!scalar(%hash), "Empty: ! scalar");
&ok(scalar(%hash) == 0, "Empty: 0 == scalar");
&ok(!keys(%hash), "Empty: ! keys");
++$hash{entry};
&ok((%hash ? 1 : 0),"Not empty: \%hash");
&ok(scalar(%hash), "Not empty: scalar");
&ok(scalar(%hash) != 0, "Not empty: scalar != 0");
&ok((keys(%hash) ? 1 : 0),"Not empty: keys");
This works in its entirety:
1..9
ok 1 - Version = v5.32.1
ok 2 - Empty: !%hash
ok 3 - Empty: ! scalar
ok 4 - Empty: 0 == scalar
ok 5 - Empty: ! keys
ok 6 - Not empty: %hash
ok 7 - Not empty: scalar
ok 8 - Not empty: scalar != 0
ok 9 - Not empty: keys
Thanks, all! | [reply] [d/l] [select] |
|
use strict;
use warnings;
use Test::More;
my $ntests = 9;
plan tests => $ntests;
{
my $expected_version = 'v5.32.1';
cmp_ok( $^V, 'eq', $expected_version, "Version = $expected_version"
+ );
}
{
my %hash;
ok( !%hash, "Empty: !\%hash" );
ok( !scalar(%hash), "Empty: ! scalar" );
cmp_ok( scalar(%hash), '==', 0, "Empty: 0 == scalar" );
ok( !keys(%hash), "Empty: ! keys" );
++$hash{entry};
ok( (%hash ? 1 : 0), "Not empty: \%hash" );
ok( scalar(%hash), "Not empty: scalar" );
cmp_ok( scalar(%hash), '!=', 0, "Not empty: scalar != 0" );
ok( (keys(%hash) ? 1 : 0), "Not empty: keys" );
}
Some points to note:
- I always prefer Test::More to Test::Simple because that scales better as test scripts grow more complex over time.
- At the top of your test script, you should explicitly declare how many tests your script intends to run, to protect against premature failure. I always use plan for this because as test scripts grow more complex over time you sometimes need to calculate this number.
- I pulled a face the instant I saw your &ok() function calling style! Haven't seen that dreadful old style for 20 years! :) From Perl Best Practices: Call subroutines with parentheses but without a leading & (item 113). Update: see also.
- Minimize the scope of variables. I find bare blocks a convenient way to do this in test scripts. Just because it is a test script doesn't mean you should drop basic standards of code quality.
- Prefer cmp_ok to ok because you get clearer diagnostics when a test fails (see below for an example).
- Don't repeat yourself. Test scripts are programs and you should follow the same coding quality standard in the test scripts as for the code under test. Doing this makes long term code maintenance more enjoyable and less error-prone.
When I first ran your test script, it failed with:
not ok 1 - Version = v5.32.1
# Failed test 'Version = v5.32.1'
# at badtests1.pl line 8.
Note that using cmp_ok instead of ok provides clearer diagnostics:
# Failed test 'Version = v5.32.1'
# at badtests2.pl line 9.
# got: 'v5.32.0'
# expected: 'v5.32.1'
See Also
| [reply] [d/l] [select] |
Re: How to test for empty hash?
by perlfan (Parson) on Aug 08, 2021 at 02:28 UTC
|
I came here to suggest scalar, as in,
perl -e 'my %x=(); print scalar %x'
0
but then, this caused me pause:
perl -e 'my %x=undef; print scalar %x'
1/8
| [reply] [d/l] [select] |
|
With all this talk of scalar, I had to look it up because,
despite using Perl heavily for twenty years, I've never actually used it!
Which reminded me that I have employed the equivalent "secret" version,
namely the infamous inchworm ~~ secret operator ...
using it three times in my highest rated node Saturn I see. :)
See also:
Note that the inchworm secret operator should not be confused with Perl's experimental smartmatch operator.
| [reply] |
|
$ perlbrew exec perl -e 'for(scalar($#foo)) { $_=3 } warn "$] ".@foo."
+\n"' >/dev/null
5.034000 4
5.032001 4
5.030003 4
5.028003 4
5.026003 4
5.024004 4
5.022004 4
5.020003 0
5.018004 0
$ perlbrew exec perl -e 'for(~~$#foo) { $_=3 } warn "$] ".@foo."\n"' >
+/dev/null
5.034000 0
5.032001 0
5.030003 0
5.028003 0
5.026003 0
5.024004 0
5.022004 0
5.020003 0
5.018004 0
Update: The example above is one of the exceptions for when ~~ is not equivalent to scalar anyway:
$ perl -le 'print scalar($#foo)'
-1
$ perl -le 'print ~~$#foo'
18446744073709551615
... but the point still stands (though interestingly, my $foo="x"; for(scalar($foo)) {$_.="y"} worked even before 5.22). | [reply] [d/l] [select] |
|
|
|
No cause for pause. undef is promoted to the empty string, used as a hash key and assigned undef as its value because that's the default value
of items in the empty list
| used by Perl when a key in a hash assignment list
has no corresponding value, or
Odd number of elements in hash assignment.
Win8 Strawberry 5.8.9.5 (32) Sat 08/07/2021 23:15:45
C:\@Work\Perl\monks
>perl
use strict;
use warnings;
use Data::Dump qw(dd);
my %h = undef;
print scalar %h, "\n";
dd \%h;
^Z
Odd number of elements in hash assignment at - line 7.
Use of uninitialized value in list assignment at - line 7.
1/8
{ "" => undef }
The hash ends up with 1 key/value pair.
Update: Clarified (I hope!) the explanation of odd-item hash assignment.
Give a man a fish: <%-{-{-{-<
| [reply] [d/l] [select] |