# Use strictures and warnings to force declaration of variables and
# catch typos and other errors. This is recommended practice.
#
use strict;
use warnings;
# Use the Data::Dumper standard module to allow us to visualise the
# data structure we have built.
#
use Data::Dumper;
# Initialise the @input array.
#
my @input = qw{
1200 1300 1200 1000 1100 1200
1500 1700 2000 2100 3000 2100
1200 1500 1700 1700
};
# Assign our groups of unique items to the @groups array; the groups
# are returned as a list of references to arrays by the deDup
# subroutine which is called with a reference to the @input array as
# a parameter.
#
my @groups = deDup( \ @input );
# Print the Data::Dumper representation of our resultant @groups
# array.
#
print Data::Dumper->Dumpxs( [ \ @groups ], [ qw{ *groups } ] );
# Declare the deDup subroutine.
#
sub deDup
{
# Assign the reference to an array passed as the sole argument
# to the $raToCheck scalar variable.
#
my $raToCheck = shift;
# Initialise two array references (also known as anonymous arrays)
# that will hold our unique items and our duplicates. Also declare
# a hash %seen that we will use to find our duplicates.
#
my $raUniq = [];
my $raDups = [];
my %seen;
# This is where we find duplicates. We use @$raToCheck to de-
# reference the array reference held in the $raToCheck scalar in
# order to access each element. Each element is passed in turn in
# the $_ scalar variable by the "for" statement modifier to the
# code to its left. That code pushes the item onto one of two
# anonymous arrays depending on whether that item has already
# been seen or not. If the item has not been seen there will not
# yet be an element in the %seen hash with a key matching that
# item so the test $seen{ $_ } ++ ? will be false, the item will
# be pushed onto @$raUniq then the ++ post-increment will ensure
# that $seen{ $_ } has a value of 1 so that, if that item is
# encountered again, the test will now be true and the item will
# be pushed onto @$raDups;
#
push @{ $seen{ $_ } ++ ? $raDups : $raUniq }, $_ for @$raToCheck;
# This is where the recursion happens. The subroutine returns the
# reference to the array of unique values and, if the anonymous
# array of duplicates is not empty, it calls itself with the
# duplicates as an argument, otherwise an empty list. So the
# returmed list is of a successively smaller series of anonymous
# arrays until there are no duplicates to be found.
#
return $raUniq, @$raDups ? deDup( $raDups ) : ();
}
|