optiontrader has asked for the wisdom of the Perl Monks concerning the following question:

I'm still a novice so please bare with me. I have one table that is essentially a long list of find and replace values. First column being a "find" value and the 2nd Column being the "replace" value. The 2nd table is the table I'd like to search in. I can't come up with anything that works.
#Table 1 contains the find and replace values; @AoA = ( [ "jones", "B0"], [ "smith", "B1"], [ "adrew", "B1"], [ "larry", "B3"], ); #the 2nd table contains the data that I want to search in. @AoA2 = ( [ "jones", "AAAAA", "BBBBB", "CCCCC"] [ "aaaaa", "AAAAA", "larry", "CCCCC"] [ "jones", "AAAAA", "BBBBBB","CCCCC"] [ "DDDDD", "AAAAA", "BBBBBB","larry"] [ "jones", "AAAAA", "andrew","CCCCC"] [ "jones", "smith", "BBBBBB","CCCCC"] );
For example, I'd like to be able to find every instance of "jones" in table two and replace it's value with "BO" regardless of its position in the table. I appologize if this is unclear and very much appreciate any help or guidence. thank you, sean

Replies are listed 'Best First'.
Re: find and replace project with values coming from a table
by Abigail-II (Bishop) on Jul 05, 2003 at 00:35 UTC
    Put the first table into a hash, and loop over the second:
    #!/usr/bin/perl use strict; use warnings; my %subs = map {@$_} [qw /jones B0/], [qw /smith B1/], [qw /adrew B2/], [qw /larry B3/]; my @data = ([qw /jones AAAAA BBBBB CCCCC/], [qw /aaaaa AAAAA larry CCCCC/], [qw /jones AAAAA BBBBB CCCCC/], [qw /DDDDD AAAAA BBBBB larry/], [qw /jones AAAAA adrew CCCCC/], [qw /jones smith BBBBB CCCCC/], ); foreach (@data) { foreach (@$_) { $_ = $subs {$_} || $_; } } $" = '", "'; print qq !["@$_"]\n! for @data; __END__ ["B0", "AAAAA", "BBBBB", "CCCCC"] ["aaaaa", "AAAAA", "B3", "CCCCC"] ["B0", "AAAAA", "BBBBB", "CCCCC"] ["DDDDD", "AAAAA", "BBBBB", "B3"] ["B0", "AAAAA", "B2", "CCCCC"] ["B0", "B1", "BBBBB", "CCCCC"]

    Abigail

      Just one nit:
      If the replacement value is not true, it fails to replace.

      I recommend replacing

      $_ = $subs {$_} || $_;
      with
      $_ = exists $subs {$_} ? $subs {$_} : $_;
      so that a 0 could also be used as a replacement value.

      Although I must say, it does somewhat damage the visual aesthetics.
      --
      Snazzy tagline here

        I choose for the former because with the given data, the problem doesn't arise. If you want to be able to cope with possible false values (except for 0, the empty string, and undef would be false too) I would use:
        $_ = $subs {$_} if exists $subs {$_};

        The effect is the same of course, but it looks better IMO.

        Abigail

Re: find and replace project with values coming from a table
by Aragorn (Curate) on Jul 05, 2003 at 08:46 UTC
    Altough Abigail-II's solution is correct (as they practically always are), it's extremely dense with Perl idiom. I think that for a novice such as you, the solution raises a lot of questions. I made a version of Abigail-II's solution which is essentially the same, but IMHO a bit more geared to novices, with respect to the use of Perl's features. If I insulted your intelligence, I apologize:
    #!/usr/bin/perl use strict; use warnings; use English; # Table 1 contains the find and replace values; my %substs = ( "jones" => "B0", "smith" => "B1", "adrew" => "B1", "larry" => "B3", ); # The 2nd table contains the data that I want to search in. my @data = ( [ "jones", "AAAAA", "BBBBB", "CCCCC"], [ "aaaaa", "AAAAA", "larry", "CCCCC"], [ "jones", "AAAAA", "BBBBBB","CCCCC"], [ "DDDDD", "AAAAA", "BBBBBB","larry"], [ "jones", "AAAAA", "andrew","CCCCC"], [ "jones", "smith", "BBBBBB","CCCCC"] ); # Loop over every element in the @data array. foreach my $row (@data) { foreach my $elem (@$row) { # $elem is an 'alias' for the value in the array, so # changing $elem changes the corresponding value in the # array. if ($substs{$elem}) { # We only want to change $elem when $elem is a key in the # substs hash. $elem = $substs{$elem} } } } # Print ", " between array values when the array is printed. $LIST_SEPARATOR = '", "'; # And show the resulting array. foreach my $row (@data) { print "[\"@$row\"]\n"; }
    I hope this helps your understanding, and also that it helps you understand some of the Perl idiom.

    Arjen

      There are a couple of minor issues with this implementation related to the last bit of code (to print out the results).

      First off, it is extremely unwise to set any punctuation vars (which $LIST_SEPERATOR happens to be, even if it doesnt look it) without utilizing a local and an enclosing scope. Even though it isnt directly an issue in the code you posted its best not to get into bad habits. (This criticism applies to Abigail-II as well. You and he probably know when this is ok and when not, but it is unlikely the OP does. :-)

      Second is the use of use English; which unless this has been fixed (I dont know if it has or not in newer perls, and certainly hasnt in older ones) then the module carries an unnacceptable performance penalty for programs that use regexes. (It triggers "saw-ampersand" which causes regexes to be signifigantly slower in ALL code used in the current running interpreter.) The general rule of thumb is to not use English but just learn the mnemomics of the punctuation vars. $" and $, are not difficult to remember IMO, even if they do look weird.

      Anyway, all that aside IMO Its better to just say

      # And show the resulting array. foreach my $row (@data) { print qq(["),join('", "',@$row),qq("]\n); }

      as it actually works out that even when you use $" or $, directly that the join statement is less chars to type:

      {local$"=','; print "@array\n";} print join(',',@array),"\n";

      And doesnt have any potential for accidental action at a distance which is what you get by setting any of the punction vars without localizing them as tightly as possible.

      Anyway, it was cool of you to produce a "beginners" version of Abigail-IIs code. ++ to you.


      ---
      demerphq

      <Elian> And I do take a kind of perverse pleasure in having an OO assembly language...
        First off, it is extremely unwise to set any punctuation vars (which $LIST_SEPERATOR happens to be, even if it doesnt look it) without utilizing a local and an enclosing scope.

        It's not possible not to have a scope in a Perl program. Remember that a file is a block as well. I'd considered using local, but then dismissed it as not necessary - the program is about to end anyway. Why have Perl save the old value of $" and restore it, if you're not going to use the old value?

        Second is the use of use English; which unless this has been fixed (I dont know if it has or not in newer perls, and certainly hasnt in older ones) then the module carries an unnacceptable performance penalty for programs that use regexes.

        use English "-no_match_vars"; is as old as at least 5.6.0, if not older. Furthermore, the program isn't using regular expressions, so the argument doesn't old anyway. There is however, IMO, a stronger argument to not use English.pm. The majority of the code, and the majority of the programmers out there don't use this module. If you use English.pm, it becomes harder for you to understand programs written by others (because you aren't used to those variables), and others find it harder to understand your programs (because others don't recognize the variables).

        Abigail