質問

I am trying to analyze a hashmap, for duplicate values and getting their keys in arrays. These arrays will be in an array of arrays. I am a newbie, by the way. However it never stops running when I start it. Where am I wrong?

while (($k,$v)=each %hashmap){
        $hasduplicate = 0;
        delete $hashmap{$k};
        @dups = ();
        while (($k1,$v1) = each %hashmap){
            if ($v1 eq $v) {
                $hasduplicate = 1;
                push @dups, $k1;
                delete $hashmap{$k1};
            }
        }
        if ($hasduplicate){
            push (@dups, $k);
            push @dupsarray, [@dups];}
}
役に立ちましたか?

解決

Each hash has just one iterator aligned to itself in Perl (see each). Therefore, running each for the same hash in a loop that calls each is not doing what you think.

If you want to see what's going on, try adding the following line at the start of the outer loop:

warn $k;

You are missing several dollar signs before variable names. For example, you probably want to delete $hashmap{$k} instead of $hashmap{k}, which is equivalent to $hashmap{'k'}.

To output an array of arrays, you have to dereference the inner arrays:

print map "@$_\n", @dupsarray;

BTW, I would use a hash of arrays to solve your task. Here's how:

my %dups;
while (my ($k, $v) = each %hashmap) {
    push @{ $dups{$v} }, $k;
}

for my $k (grep @{ $dups{$_} } > 1, keys %dups) {
    print "$k: @{ $dups{$k} }\n";
}

他のヒント

The problem is that there can be only one each sequence per hash, as there is only a single index to keep track of the next key/value pair.

In addition, you are using k and k1 in a few places where you mean $k and $k1. You must always use strict and use warnings at the top of every Perl program. This would have alerted you to the problem.

You can get around this problem by using for my $k1 (keys %hashmap) { ... } for the inside loop. This will create a separate list of keys to assign to $k1 in turn so that there is no multiple use of the iterator.

This modification of your code does what I think you want.

use strict;
use warnings;

my %hashmap = (
  a => 'a',
  b => 'b',
  c => 'a',
  d => 'c',
);

my @dupsarray;

while (my ($k, $v) = each %hashmap) {
  my $hasduplicate = 0;
  delete $hashmap{$k};
  my @dups;

  for my $k1 (keys %hashmap) {
    my $v1 = $hashmap{$k1};
    if ($v1 eq $v) {
      $hasduplicate = 1;
      push @dups, $k1;
      delete $hashmap{$k1};
    }
  }

  if ($hasduplicate) {
    push(@dups, $k);
    push @dupsarray, [@dups];
  }

}


use Data::Dump;
dd \@dupsarray;

output

[["a", "c"]]

A much simpler method is to create an inverted hash where the keys and values of the original hash are swapped. Then just pick out the values of the inverted hash that have more than one element. This program demonstrates

use strict;
use warnings;

my %hashmap = (
  a => 'a',
  b => 'b',
  c => 'a',
  d => 'c',
);

my @dupsarray = do {
  my %inverted;
  while (my ($k, $v) = each %hashmap) {
    push @{ $inverted{$v} }, $k;
  }
  grep { @$_ > 1 } values %inverted;
};


use Data::Dump;
dd \@dupsarray;

output

[["c", "a"]]
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top