Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Throw compilation error(or warning) if duplicate keys are present in hash

by I_love_perl (Novice)
on Nov 24, 2011 at 07:17 UTC ( [id://939809]=perlquestion: print w/replies, xml ) Need Help??

I_love_perl has asked for the wisdom of the Perl Monks concerning the following question:

Hi, I've a pm file which has a static hash like this

use strict; use warnings; %hash = ( "one" => "1", "two" => "2", "one" => "3", );

Here the key "one" is repeated.When I do a perl -c file.pm, it compiles fine.

Is there any way to invoke perl to throw an error(or warning) during the compilation in case of usage of duplicate keys in the hash ?

  • Comment on Throw compilation error(or warning) if duplicate keys are present in hash
  • Download Code

Replies are listed 'Best First'.
Re: Throw compilation error(or warning) if duplicate keys are present in hash
by ikegami (Patriarch) on Nov 24, 2011 at 07:50 UTC

    There is no duplicate key at compile-time — the list is only created and assigned to the hash at run-time — so the answer to your literal question is "no".

    However, it is possible to write an opcode checker that detects the posted code pattern at compile-time. (For example, module autovivification uses an opcode checker to not only examine opcodes, but to replace them.) This approach would probably be better to add a *run-time* warning.

    A better approach to detecting the situation at compile-time would be to write a Perl::Critic rule to look for the code pattern you posted.

      Hi Ikegami, Thanks for your quick response.

      I've tried using Perl::Critic (http://perlcritic.com/), but it is not throwing up any warning with respect to the re-occurrence of the hash key. Correct me if I'm doing something wrong in using Perl::Critic

        I didn't say a rule already existed to check this. Quite the opposite, I assumed there wasn't one and told you you'd have to actually write the rule.

Re: Throw compilation error(or warning) if duplicate keys are present in hash
by ricDeez (Scribe) on Nov 24, 2011 at 08:27 UTC

    The problem is that your %hash is valid as Perl will just store the last value for key "one". Perhaps this would work with Hash::MultiValue? I am thinking of something like this:

    use strict; use warnings; use Hash::MultiValue; use 5.012; my $mhash = Hash::MultiValue->new( "one" => "3", "two" => "2", "two" => "1" ); die "Duplicate key detected for \%hash" if grep { my @a = $mhash->get_ +all($_); @a > 1 } $mhash->keys; print "We're good!";

    I have not used it in anger myself but it would seem as though it would work for what you want!

Re: Throw compilation error(or warning) if duplicate keys are present in hash
by afoken (Chancellor) on Nov 24, 2011 at 12:57 UTC

    What about tie?

    #!/usr/bin/perl { package AssignOnce; use strict; use warnings; use Carp (); use Tie::Hash; use parent -norequire => 'Tie::StdHash'; # why, oh why, is Tie::St +dHash hidden in Tie::Hash? sub STORE { my ($self,$key,$value)=@_; if (exists $self->{$key}) { Carp::carp("Overwriting hash key '$key', old value: '$self +->{$key}', new value: '$value'"); } $self->SUPER::STORE($key,$value); } } use strict; use warnings; use Data::Dumper; our %h; tie %h,'AssignOnce'; %h=( one => 1, two => 2, one => 3, ); print Dumper(\%h);

    Result:

    Overwriting hash key 'one', old value: '1', new value: '3' at tie.pl l +ine 29 $VAR1 = { 'one' => 3, 'two' => 2 };

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re: Throw compilation error(or warning) if duplicate keys are present in hash
by Util (Priest) on Nov 24, 2011 at 15:21 UTC
    This will warn when run with or without `-c`:
    sub no_dup_keys { die "Odd number of elements" if @_ % 2; my %h; while ( my ( $key, $new_val ) = splice @_, 0, 2 ) { if ( exists $h{$key} ) { warn "Duplicate key: $key, old_value: $h{$key}," . " new_value: $new_val\n "; next; # Remove for rolling overlay. } $h{$key} = $new_val; } return %h; } my %hash; BEGIN{ %hash = no_dup_keys( "one" => "1", "two" => "2", "one" => "3", ); }
Re: Throw compilation error(or warning) if duplicate keys are present in hash
by TJPride (Pilgrim) on Nov 24, 2011 at 08:47 UTC
    Thinking outside the box - is it possible to modify whatever is generating this .pm (I'm assuming it's generated, otherwise you could easily fix the problem...) to check for duplicate keys up front and eliminate them or deal with them somehow? Seems overly complex to mess with them on the far end.

      Hi TJPride,

      Thanks for your response

      It is a manually generated .pm file by the people who are not very much familiar with perl syntax. Hence I suspect that there might be a good chance of mistyping an existing key.

      In this case we are not looking for eliminating the duplicated hash keys, rather we are looking for updating .pm file properly with a new key for the duplicated one's.

      So we need to know if some key is duplicated

      As I know writing a small validating perl script will do this , but I was thinking if there is any approach available in perl by default or addon.

        It is a manually generated .pm file by the people who are not very much familiar with perl syntax. Hence I suspect that there might be a good chance of mistyping an existing key.
        You could get those people into assigning not to %hash, but to @hash, i.e. an array instead. Later in the program, you check @hash whether a "key" occurs twice, before assigning to %hash.

        -- 
        Ronald Fischer <ynnor@mm.st>
        To implement rovf's suggestion:

        use Data::Dumper; use strict; use warnings; my %hash; assignWithDupes([ "one" => "1", "two" => "2", "one" => "3", "three" => "4", "two" => "5", "one" => "6", ], \%hash); print Dumper(\%hash); sub assignWithDupes { my ($arr, $hash) = @_; my ($i, $j, $key, $val); for ($i = 0; $i < $#$arr; $i += 2) { $key = $arr->[$i]; $val = $arr->[$i+1]; if (!defined $hash->{$key}) { $hash->{$key} = $val; next; } for ($j = 2; defined $hash->{"${key}_$j"}; $j++) {} $hash->{"${key}_$j"} = $val; } }

        Output (modified to put it in order):

        $VAR1 = { 'one' => '1', 'one_2' => '3', 'one_3' => '6' 'two' => '2', 'two_2' => '5', 'three' => '4', };

        As you can see, successive instances of the same key are assigned to _2, _3, etc.

Re: Throw compilation error(or warning) if duplicate keys are present in hash
by Marshall (Canon) on Nov 24, 2011 at 10:44 UTC
    There is no way that I know of to tell Perl that assigning a different value to a hash key is an error. That is because that is defined in the language to be completely ok!

    If you want checking like that, then the way to do it is to read the data like it came from an external file, build the hash and report errors as the data is processed.

    There are many formulations of how to do this. I present one below.

    #!/usr/bin/perl -w use strict; use Data::Dumper; my $hash_data = <<END; #this is a "here is" string one 1 two 2 one 3 END my %seen; my %hash = map{ my ($text, $number) = split; warn "\"$text\" has been re-defined!\n" if $seen{$text}++; ($text,$number); }split (/\n/, $hash_data); print Dumper \%hash; __END__ Prints: "one" has been re-defined! $VAR1 = { 'one' => '3', 'two' => '2' };

      Hi Marshall,

      Thanks for your response.

      You are right in saying that the re-definition of a key with a new value is perfectly fine.

      But this leads to a anomaly called RD (Re-Definition). The RD means re-defining a variable with a new value without using it's initial value (They say to use your later value as initial one than initializing the variable to something else and changing the variable's value without using it's initial value). The coding guide lines say to identify such cases and rectify the same.

      This is one of the reason why I'm thinking of getting a warning or error from standard perl or any of it's additional packages (like use strict,warnings)

Re: Throw compilation error(or warning) if duplicate keys are present in hash
by ansh batra (Friar) on Nov 24, 2011 at 08:03 UTC
    use strict; use warnings; my %hash = ("one" => "1", "two" => "2", "one" => "3", ); foreach my $key ( keys %hash ) { print $key, " => ", $hash{$key}, "\n"; }
    output
    one => 3 two => 2
    unable to track the dublicate key.filtration done by perl itself
    nice point by i_love_perl
      %hash = EXPR;
      does something like
      my @anon = LIST; %hash = (); while (@anon) { my $k = shift(@anon); my $v = shift(@anon); $hash{$k} = $v; }

      So if you have two values for the same key, the latter prevails.

      (There's no actual @anon created; the stack is used.)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://939809]
Approved by davido
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others perusing the Monastery: (2)
As of 2024-04-25 22:02 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found