Walt Howard
Walt Howard

Reputation: 8208

Is there a way to convert a perl array to a hash using a the map {} operator because a simple assignment isn't enough

I'm writing a better version of sed basically (I need it for some ETL work).

cat file.txt | transform [regex] [replaceor] [regex] [replaceor] [regex replaceor]... 

my %transforms = @ARGV; # convert array (regex,replace,regex,replace) pairs into hash
# want something like this:
# my %transforms = map { qr/$_/ => $_[++]} @ARGV; # grab TWO element of @ARGV at a time

while (my $content = <STDIN>) {
   while (my ($scan, $print) = each(%transforms)) {
   # $print could be code. Still deciding on that.
   my $scan = qr/$scan/;  # WANT TO AVOID re-compiling the regex every time
   my $transformed = $content =~ s/$scan/$print/re; #                                                          
   print $transformed;
  }
}

Yes, I could do this with brute force and many other ways but this grabbing multiple items from an array has come up for me several times and I wonder if there is a trick to it. Hmm. What about a double map?

Upvotes: 1

Views: 121

Answers (2)

ikegami
ikegami

Reputation: 386646

You could use

use List::Util qw( pairmap );

my %transforms = pairmap { qr/$a/ => $b } @ARGV;

or

my %paired_args = @ARGV;

my %transforms = map { qr/$_/ => $paired_args{$_} } keys( %paired_args );

But hash keys are always strings. The above is equivalent to

use List::Util qw( pairmap );

my %transforms = pairmap { my $re = qr/$a/; "$re" => $b } @ARGV;

And that is effectively equivalent to the following (or something similar):

my %transforms = pairmap { "(?^u:$a)" => $b } @ARGV;

You're hoping to compile the patterns once each, but this doesn't achieve that. You're actually causing each pattern to be compiled one extra time!

You don't actually need to look up values by key, so an array of arrays would do the trick here.

use List::Util qw( pairmap );

my @transforms = pairmap { [ qr/$a/, $b ] } @ARGV;

while ( my $content = <STDIN> ) {
   for ( @transforms ) {
      my ( $scan, $print ) = @$_;
      $content =~ s/$scan/$print/;
   }

   print $content;
}

Note how I didn't use /e. If you were using /e in the hopes of getting $1 to work, that's not the right approach. Use String::Substitution instead.

For example, you can replace

# Doesn't support $1 and such. Doesn't require `\` to be escaped.
$content =~ s/$scan/$print/;

with

use String::Substitution qw( sub_modify );

# Supports $1 and such. Requires `\` to be escaped.
sub_modify( $content, $scan, $print );

Upvotes: 2

toolic
toolic

Reputation: 62236

pairmap grabs 2 items at a time from an array:

use List::Util qw(pairmap);
my %transforms = pairmap { qr/$a/, $b } @ARGV;

Upvotes: 2

Related Questions