Re: scanning hash
by ysth (Canon) on Aug 01, 2004 at 09:06 UTC
|
keys(%{{reverse %hash}}) <= 1
From the inside out:
- %hash in list context returns a list of key,value,key,value
- reverse LIST in list context reverses the list
- { LIST } returns a ref to an anonymous hash with the given list of keys and values
- %{ HASHREF } gets the hash given the reference
- keys %HASH in scalar context returns a count of the keys
Because the anonymous hash is built with keys as the values of the original hash and values as the keys of the original hash, and identical later keys supercede earlier keys, it will have only one key for each unique value from the original hash. So if it ends up with 1 key all the values of the original hash were equal (and if it ends up with 0 keys, the original hash was empty, which may or may not count as having all values equal). | [reply] [d/l] [select] |
|
That works, is brief, and hip. It is possibly not however the most efficient solution as it fails to utilize an iterative, fail fast approach and requires generating and manipulating un-used anon data stuctures. Given the task we can look at less of the hash if we simply fail fast as soon as we detect a non matching value. Depending on the context this may or may not matter. Still a very cool solution though. Wish I had of thought of it. What the hell, I will next time :-)
print ident( { foo=>1, bar=>1, baz=>1 } );
sub ident {
my $value = (values %{$_[0]})[0];
for (values %{$_[0]} ) {
return 0 unless $value eq $_;
}
return 1;
}
| [reply] [d/l] |
|
sub ident {
my $value = ( values %{ $_[0] } )[0];
for ( values %{ $_[0] } ) {
no warnings 'uninitialized';
return 0 unless !( defined $value xor defined $_ ) and ( $valu
+e eq $_ );
}
return 1;
}
It would be better if I could think of a way to skip the $value eq $_ test when both variables are undefined, and still have the entire expression be true, but I can't see a way to do that without an extra test for definedness on one of the values.
Update after bageler's reply: this was wrong:
return 0 unless ( defined $value xor defined $_ ) and ( $value eq $_ )
+;
The condition will only ever be true when comparing an undef with an empty string, but in no other case, because the left expression is only true if the operands are unequal, ie if only one of them is defined. In that case, the right expression can only also be true if the defined value is an empty string.
Makeshifts last the longest.
| [reply] [d/l] [select] |
|
|
|
use strict;
use warnings;
my %goodhash = qw/one 1 two 1 three 1 four 1 five 1/;
my %badhash = qw/one 1 two 2 three 2 four 2 five 2/;
foreach my $testhash ( \%goodhash, \%badhash ) {
print SameVals( $testhash )
? "Good.\n"
: "Bad.\n";
}
sub SameVals {
return keys( %{ { reverse %{ $_[0] } } } ) <= 1;
}
Note that this adds a hashref dereference. That's the %{ $_[0] } part.
| [reply] [d/l] [select] |
|
| [reply] |
|
| [reply] |
|
|
|
Re: scanning hash
by davido (Cardinal) on Aug 01, 2004 at 09:47 UTC
|
I just wanted to add that ysth's solution can also be adapted in such a way that it can be applied to arrays.
Determine if all elements in @array are equal.
use strict;
use warnings;
my @goodarray = ( 1, 1, 1, 1, 1 );
my @badarray = ( 1, 2, 3, 4, 5 );
foreach my $aref ( \@goodarray, \@badarray ) {
print SameVals($aref)
? "Good!\n"
: "Bad!\n";
}
sub SameVals {
keys %{ { map { $_ => undef } @{ +shift } } } <= 1;
}
Enjoy!\n
| [reply] [d/l] |
|
| [reply] |
Re: scanning hash
by BrowserUk (Patriarch) on Aug 01, 2004 at 11:16 UTC
|
use List Util qw[ reduce ];
print 'They\'re all the same!'
if reduce{ $a && $a eq $b ? $a : () } values %hash;
Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail
"Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
| [reply] [d/l] |
|
Very nice. But assumes that all values are true. It would be fairly easy to make it work with 0 or "" values, but not so easy to make it work with undef.
| [reply] |
Re: scanning hash
by ysth (Canon) on Aug 01, 2004 at 11:46 UTC
|
Here's a scalable, undef-friendly solution, just for kicks:
my $multiple_values;
my ($k,$v);
use Data::Dumper;
$Data::Dumper::Useqq = 1;
while (defined($k = each %hash)) {
if ($v) {
++$multiple_values, last if Dumper($hash{$k}) ne $v;
} else {
$v = Dumper($hash{$k})
}
}
It does distinguish between strings and numbers that eq would say were the same, though. | [reply] [d/l] |
|
| [reply] |
Re: scanning hash
by wfsp (Abbot) on Aug 01, 2004 at 09:28 UTC
|
#!/bin/perl5
use strict;
use warnings;
my %hash = ( key1 => 0,
key2 => 0,
key3 => 0);
my %dups = reverse %hash;
my $count = keys %dups;
print "$count\n";
$count = 1
Beaten to the post of course. This is just in case you wanted to do it on two lines instead of one! | [reply] [d/l] |
|
| [reply] |