First, include grep in the foreach so that we can take a closer look at it. I am going to expand some idioms into larger designs for more clarity.
my @all_matches = ( ... ); { my %seen; my @no_dupes; foreach my $match ( @all_matches ) { my $first_item = $match->[0]; my $second_item = $match->[1]; my $third_item = $match->[5]; my $key = join '-', $first_item, $second_item, $third_item; if( not $seen{ $key }++ ) { push @no_dupes, $match; } } @all_matches = @no_dupes; }
In other words, the source encoder creates a hash key using a reference to the array contained in $ match for each of the reference indices $match->[0] , 1 and 5 . Since the hash keys are unique, any duplicates will be deleted, checking if the key exists before pressing @no_dupes .
The grep{} mechanism is simply more efficient code (i.e., a faster type and no variables) to accomplish the same thing. If this works, why reorganize it? What does this not do, what do you need to improve?
To do the same with HoH, you can do this:
my @all_matches = ( ... ); { my %seen; my @no_dupes; foreach my $match ( @all_matches ) { my $first_item = $match->[0]; my $second_item = $match->[1]; my $third_item = $match->[5]; if( not $seen{ $first_item }->{ $second_item }->{ $third_item }++ ) { push @no_dupes, $match; } } @all_matches = @no_dupes; }
What can be translated back to grep as follows:
my @all_matches = ( ... ); { my %seen; @all_matches = grep { not $seen{$_->[0]}->{$_->[1]}{$_->[5]}++ } @all_matches; }
However, this is the case when I do not see a clear advantage for building a data structure, if you are not going to use %seen later for something else.
Regarding the operator || this is another animal. I cannot think of any useful way to use it in this context. The logical short circuit operator, say, " $a || $b || $c ", checks the logical veracity of $a . If it is true, it returns its value. If it is wrong, it checks $b in the same way. If it is wrong, it checks $c in the same way. But if $a true, $b never checked. If $b true, $c never checked.