http://www.perlmonks.org?node_id=379036


in reply to scanning hash

keys(%{{reverse %hash}}) <= 1
From the inside out: 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).

Replies are listed 'Best First'.
Re^2: scanning hash
by tachyon (Chancellor) on Aug 01, 2004 at 10:38 UTC

    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; }

    cheers

    tachyon

      You'll have to extend that a bit so it properly deals with undef vs empty string.

      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.

        need to change that slightly so it will return true if all the keys are undef:
        return 0 unless defined $_ ? $_ eq $value : defined $value ? 0 : 1
Re^2: scanning hash
by davido (Cardinal) on Aug 01, 2004 at 09:20 UTC

    I love that solution. Here it is in the form of a sub. Pass a reference to the hash into the sub. A return value of true means all values are equal. A value of false means there are some not-equal values.

    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.


    Dave

Re^2: scanning hash
by Aristotle (Chancellor) on Aug 01, 2004 at 14:40 UTC
    This code is incorrect. It will treat undefs and empty strings as the same.

    Makeshifts last the longest.

      Undefs and empty strings are equal. The OP wanted to check if all values are equal.

        Oh dear. Next thing we will argue about whether the equality of two values can be determined by the equality of their string representations. :-)

        Makeshifts last the longest.