Reputation: 355
I am looking for some help in one senario.
Requirement:
Through some validation, I have strored following kinds of value in one array.
@array_name = ("Rajesh","Raju","Ram","John","peter");
Now I know from some background that "Rajesh", "Ram", "peter", are duplicate entries so I would expect my output to be:
@array_name = ("Rajesh","Raju","John");
# or
@array_name = ("Ram","Raju","John");
# or
@array_name = ("peter","Raju","John");
I have done example program such as below but it does not satisfy me...
my $spcific_output ="";
my $output ="";
foreach my $name (@array_name)
{
if($name eq "Rajesh" || $name eq "Ram" || $name eq "peter")
{
$spcific_output = "Rajesh and Ram and peter");
}
else
{
$output .= "My Name is $name";
}
}
$output .= $spcific_output;
Any best way to achieve this?
Upvotes: 2
Views: 211
Reputation: 139451
With Perl, whenever you want unique values from some collection, think about how you can use a hash to help you automatically collapse duplicates or at least help you remember which values you have already seen. For example, see How can I get the unique keys from two hashes? in section 4 of the Perl FAQ.
Your case is a little trickier because you have sets of interchangeable names, so you must record this information.
sub add_names {
my $equivalent = shift;
for (@_) {
my @names = map lc, @$_;
for (@names) {
die "$0: overlap on name '$_'" if exists $equivalent->{$_};
$equivalent->{$_} = \@names;
}
}
$equivalent;
}
Here, $equivalent
is a reference to a hash. After calling
add_names $equivalent, [ qw/ Rajesh Ram peter / ];
the hash will have keys 'rajesh'
, 'ram'
, and 'peter'
whose values are all [ 'rajesh', 'ram', 'peter' ]
. Structuring it this way means we can get to the full set of names no matter which name we encounter first.
Note also that you can stack multiple sets of names in a single call, as in
add_names $equivalent, [ qw/ Rajesh Ram peter / ],
[ qw/ Jim Bob Bubba / ];
With the names mapped out, we can now process a list and keep the first name from each set that we find. For a given name, check whether we have seen it or any of its equivalents previously. If we have not seen it, save the name and mark all equivalents as seen.
sub remove_duplicates {
my $equivalent = shift;
my %seen;
my @uniques;
foreach my $name (@_) {
my $normal = lc $name;
unless ($seen{$normal}) {
push @uniques, $name;
++$seen{$_} for @{ $equivalent->{$normal} };
}
}
wantarray ? @uniques : \@uniques;
}
The wantarray
bit at the bottom is a common Perl idiom for adapting the return value to the calling context. If the caller wants an array, we return the array. If not, we return a scalar, namely a reference to our array of unique names.
Putting it all together makes
my $equivalent = {};
add_names $equivalent, [qw/ Rajesh Ram peter /];
my @array_name = ("Rajesh","Raju","Ram","John","peter");
print $_, "\n" for remove_duplicates $equivalent, @array_name;
Output:
Rajesh Raju John
Upvotes: 2
Reputation: 7579
If you're using v5.10 or higher you can use smart matching on an array with your duplicate names:
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my @names = qw(Rajesh Raju Ram John Peter);
my @dupl = qw(Rajesh Ram Peter);
my $seen;
my @names = grep {$_ ~~ @dupl ? !$seen++ : 1} @names;
print Dumper \@names;
Output:
$VAR1 = [
'Rajesh',
'Raju',
'John'
];
The condition in grep
evaluates !$seen++
if a name from @names
is in @dupl
, and keeps $_
only when $seen
is 0. Otherwise 1
(true
) is evaluated and $_
is kept.
Upvotes: 2