Condense this code, perhaps with the map function?

I'm trying to condense some code, hopefully by using fancy one-liners.

The whole idea is to take two columns at a time from
@AoA, starting with $startcol (a column index).  From those two
columns, create a map so that 1 is assigned to the lowest value,
2 to the next lowest, and so on.  The columns do not need to be
considered distinct.  They could be combined into a single array.

For example, a snapshot of 2 columns would be:

2 4
1 4
2 3
6 3
6 2
...

The map created for these two columns would be:

# orig value => assigned value
1 => 1
2 => 2
3 => 3
4 => 4
6 => 5
...

I would like to have this condensed by using some "one-liners", or more elegant code.
For example, perhaps the perl map function could be involved.  I definitely want
to eliminate redundancy, which you can see in the code below when it walks
over each array of AoA multiple times.

Basically, the entire portion of code I want to condense is:

#
# Start of code
#

use strict;

my @AoA = (); # Array of arrays;
my @printarray = ();
my $startcol = 5;  # column index of the start column
my $outfile = "temp.out";

# Read in the data file
while (<>) {
  chomp;
  push @AoA, [ split ];
}

# Walk over every other item starting with $startcol.
# Each row has the same number of columns.
for ( $cursor = $startcol; $cursor < $#{ $AoA[0] }; $cursor +=2 ) {

  my %hash = ();
  my %map = ();

  my $a1index = $cursor;
  my $a2index = $cursor + 1;

  # I don't need to know the number of occurrences but this counts them anyway.
  for my $i ( 0 .. $#AoA ) {
     $hash{$AoA[$i][$a1index]}++; # a1
     $hash{$AoA[$i][$a2index]}++; # a2
  }

  my $index = 1;
  foreach my $key ( sort {$a <=> $b} keys %hash ) {
     $map{$key} = $index;
     $index++;
  }

  for my $i ( 0 .. $#AoA ) {
     my $a1 = $AoA[$i][$a1index];
     my $a2 = $AoA[$i][$a2index];

     if ( exists $map{$a1} ) {
        $a1 = $map{$a1};
     }
     else {
        print "Error:  A mapped value was not found for $a1\n";
        exit;
     }

     if ( exists $map{$a2} ) {
        $a2 = $map{$a2};
     }
     else {
        print "Error:  A mapped value was not found for $a2\n";
        exit;
     }

     push @{ $printarray[$i] }, "$a1", "$a2";
  }
}

open( OUTFILE, ">$outfile" );
foreach my $i ( 0 .. $#AoA ) {
   # Print the columns before the start column
   foreach my $j ( 0 .. $startcol-1 ) {
      print OUTFILE "$AoA[$i][$j] ";
   }
   # Print the mapped values
   print OUTFILE "@{$printarray[$i]}\n";
}
close( OUTFILE );
print "The file $outfile has been created.\n";

#
# End of code
#
mock5cAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

ozoCommented:
@hash{(split)[$cursor,$cursor+1]}=() while <>;
@map{sort{$a<=>$b}keys %hash}=1..keys %hash;

wlfsCommented:
ozo is showing ingenuity as usual.
The problem is, that reading in the first two columns under consideration consumes the entire file already. And the replacement of the values in the printarray can be condensed as well.

I prepared the following code, which does the same job as the code in the question. Not quite as ingenious as ozo I'm afraid, but condensed at least :)

my @AoA;
my $startcol = 2;  # column index of the start column
my $outfile = "temp.out";

chomp, push @AoA, [ split ] while (<>);

for (my $col = $startcol; $col < $#{ $AoA[0] }; $col +=2 ) {
  my (%seen, %map);
  @map{sort {$a<=>$b} grep {!$seen{$_}++} map @{$_}[$col..$col+1], @AoA} = 1..2*@AoA;
  @{$_}[$col..$col+1] = @map{@{$_}[$col..$col+1]} foreach @AoA;
}

open(OUTFILE, ">$outfile");
print OUTFILE join(" ", @$_), "\n" foreach @AoA;
close(OUTFILE);

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
ozoCommented:
use strict;

my @AoA = (); # Array of arrays;
my @printarray = ();
my $startcol = 5;  # column index of the start column
my $outfile = "temp.out";

chomp, push @AoA, [ split ] while <>;

for( my $col = $startcol; $col < $#{ $AoA[0] }; $col +=2 ){
  my (%seen, %map);
  @seen{map @{$_}[$col..$col+1], @AoA}=();
  @map{sort{$a<=>$b}keys %seen} = 1..keys %seen;
  for( @AoA ){ $_ = $map{$_} for @{$_}[$col..$col+1] }
}

open(OUTFILE, ">$outfile");
print OUTFILE "@$_\n" for @AoA;
close(OUTFILE);
mock5cAuthor Commented:
Exactly the kind of elegant code I was looking for.  Both answers taught me a bit about what Perl is capable of doing.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Perl

From novice to tech pro — start learning today.