Q:

Perl sort on numbers

I have some perl script to process a file (contains lots of numbers) line by line. 
File content (sample data, first 3 numbers are seperated by space, then the seperate is tab between 3rd and 4th numbers):
1 2 3 15
2 9 8 30
100 106 321 92
9 8 2 59
300 302 69 88
....

Script content: 
# snippet of script
open(INF, "$infile") || die "Unable to open file $infile: $!\n";
@content = <INF>;
close(INF);

foreach $line (@content) {
    # blah blah, script to handle math here
    # Now the numbers are stored in separate variables
    # $n1 stores the 1st number, i.e.: 1
    # $n2 stores the 2nd number, i.e.: 2
    # $n3 stores the 3rd number, i.e.: 3
    # $n4 stores the 4th number, i.e.: 15
    # Solution code to be inserted here
}

I would like to:

Sort the variables $n1, $n2, $n3 and output them in ascending order.
At the end of foreach, get rid of duplicates

My approach:
# Insert below code to foreach
$numbers{$n1} = 1;
$numbers{$n2} = 1;
$numbers{$n3} = 1;
@keys = sort { $numbers{$b} <=> $numbers{$a} } keys %numbers;
#push @numbers, "$keys[0] $keys[1] $keys[2]";
$numbers2{"$keys[0] $keys[1] $keys[2]"} = 1;

This defines two hashes: 1st hash is for sorting, 2nd hash is for removing duplicates after sorting.
Is there any better approach?
Thanks,

A:

Updated with another solution -- it is the lines that may be duplicate, not numbers on a line.

In order to remove duplicate lines it is easiest if we have all sorted lines of three numbers in an array. Then we post-process that by running them through uniq. There are (at least) two ways to do this.

Store lines in an array, each being a reference to a sorted array with the three numbers. Then for comparison construct a string of each on the fly.  This is better if there is yet other processing of numbers somewhere along, as they are in arrays.
Build a string out of each sorted line and store those in an array. Then it's easier to compare.

Below I use the first approach, assuming that there is other processing of numbers.
use warnings;
use strict;
use feature wq(say);
use List::MoreUtils qw(uniq);

my $file = 'sort_nums.txt';
my @content = do {
    open my $fh, '<', $file  or die "Can't open $file: $!";
    <$fh>;
};

my @linerefs_all;
foreach my $line (@content) {
    # Calculations ... numbers stored in ($n1, $n2, $n3, $n4)
    my ($n1, $n2, $n3) = split '\s+' $line;   # FOR TESTING 
    # Add to @rlines a reference to the sorted array with first three
    push @linerefs, [ sort { $a <=> $b } ($n1, $n2, $n3) ];
}
# Remove dupes by comparing line-arrays as strings, then remake arrayrefs
my @linerefs = map { [ split ] } uniq map { join ' ', @$_ } @linerefs_all;
say "@$_" for @linerefs;

Using the posted lines in the file sort_nums.txt, the code above prints

1 2 3
2 8 9
100 106 321
69 300 302

Explanation of the post-processing line, read from the right. 

The map on the right processes a list of arrayrefs. It dereferences each and joins its elements with a space, forming a string for the line. It returns a list of such strings, one per line.
That list is pruned of duplicates by uniq, which itself returns a list, fed into the map on the left. 
In that map's block each string is split by (the default) white space into a list (of numbers on the line), and then a reference of that is taken by [ ]. This map thus returns a list of references to arrays, one for each line, what is assigned to @linerefs.

This is then printed.
If this is too much to stomach for one statement break the process into steps, generating intermediate arrays. Or switch to the second method above.

Initial post, assuming that numbers on each line may be duplicates 
I take the objective to be: sort three variables and keep only unique ones, for each line.
use List::MoreUtils qw(uniq);

foreach my $line (@content) {
    # Calculations, numbers stored in ($n1, $n2, $n3, $n4)
    my @nums = uniq sort { $a <=> $b } ($n1, $n2, $n3);
    say "@nums";
}

Remember that after this you don't know which one(s) of $n1, $n2, $n3 may have been dropped.

If, for some reason, a non-core module is not suitable, see this in perlfaq4  For example, 
my %seen = ();
my @nums = sort { $a <=> $b } grep { ! $seen{$_}++ } ($n1, $n2, $n3);

or, if you need it without an extra hash around
my @nums = do { my %seen; sort { $a <=> $b } grep { !$seen{$_}++ } ($n1, $n2, $n3) };

