Beefy Boxes and Bandwidth Generously Provided by pair Networks Bob
Problems? Is your data what you think it is?
 
PerlMonks  

Replacing values in an array

by tonto (Pilgrim)
on Jan 26, 2013 at 22:49 UTC ( #1015541=perlquestion: print w/ replies, xml ) Need Help??
tonto has asked for the wisdom of the Perl Monks concerning the following question:

I am trying to renumber an array. I want the duplicated values in the original array to have the same new values in the renumbered array, and the first value should be changed to 1 and the next to 3 and so on, using odd numbers. The order of the array must be preserved. Here is my code:
#!/usr/bin/perl use strict; use warnings; use Data::Dumper; my @array = ("M94202", "M94150", "M94297", "M94150", "M94161", "M94161 +", "M94162"); my $z = 1; foreach my $item (@array) { if ($item =~ m/M/g) { my $uniqitem = $item; foreach $uniqitem (@array) { $uniqitem =~ s/$uniqitem/$z/g; $z = $z +2; } } } print Dumper \@array; print "\n";
The output I am getting is (1,3,5,7,9,11,13) What I want is (1,3,5,3,7,7,9) Where have I gone wrong, please?
Thank you for reading!

Comment on Replacing values in an array
Download Code
Re: Replacing values in an array
by toolic (Chancellor) on Jan 26, 2013 at 23:07 UTC
    Use a hash:
    use strict; use warnings; use Data::Dumper; my @array = ("M94202", "M94150", "M94297", "M94150", "M94161", "M94161 +", "M94162"); my %uniq; my $z = 1; for (@array) { if (/M/) { if (exists $uniq{$_}) { $_ = $uniq{$_}; } else { $uniq{$_} = $z; $_ = $z; $z += 2; } } } print Dumper(\@array); __END__ $VAR1 = [ 1, 3, 5, 3, 7, 7, 9 ];
      Thank you, it WORKS !!!!!

      I really must overcome my fear of hashes now, thank you also for that encouragement.
      -tonto
Re: Replacing values in an array
by eyepopslikeamosquito (Canon) on Jan 26, 2013 at 23:26 UTC

    When dealing with duplicates in Perl, you should normally use a hash (perldoc -q duplicate). My solution (written before seeing toolic's) is essentially the same as his, though I excluded the check for "M" since all your test data contains "M".

    use strict; use warnings; use Data::Dumper; my @array = ("M94202", "M94150", "M94297", "M94150", "M94161", "M94161 +", "M94162"); my %seen; my $z = 1; foreach my $item (@array) { if (exists $seen{$item}) { $item = $seen{$item}; } else { $seen{$item} = $z; $item = $z; $z += 2; } } print Dumper \@array; print "\n";

      Seeing the same solution written differently makes it more clear to me, I am very grateful to you. I will study hashes until I get them through my thick skull! I have spent days on this.
      Again, my sincere thanks!
      -tonto

        You can consider a hash to be much like an array, except instead of numerical indexes to access individual elements, you use strings as keys.

        # Define an array: my @basket = qw(apple banana cherry); # Get an element from the array: # (remember that indexes are 0-based) print "The second kind of fruit in the basket is $basket[1]\n"; # Change an element: $basket[1] = "date"; print "Now it is $basket[1]\n";

        The above example shouldn't be unfamiliar. Now, instead of keeping a @basket that tells us what kinds of fruit we have in the basket, let's keep a %basket that can also tell us how much of that kind of fruit we have.

        # Define the hash: my %basket = ( apple => 12, banana => 6, cherry => 32, # This final comma is optional, ); # but makes it easier to add more lines in the f +uture. # Get an element from the basket: print "There are $basket{cherry} cherries in the basket.\n"; # Modify elements: $basket{cherry}--; print "Now there are $basket{cherry}.\n"; $basket{banana} *= 2; print "Double Banana Bonus! $basket{banana} bananas in the basket!\n"; $basket{apple} = 10; # Add an element: $basket{date} = 16; # Get all keys in the hash: print "Fruits in my basket: ", join(", ", sort keys %basket), "\n"; # Using a variable as a key: for my $fruit (sort keys %basket) { print "You want a(n) $fruit? I have $basket{$fruit} in my basket.\ +n"; } # The 'each' function: while (my ($fruit, $amount) = each %basket) { print "There are $amount ${fruit}s in my basket.\n"; } # Getting rid of an element: delete $basket{apple}; print "Fruits in my basket: ", join(", ", sort keys %basket), "\n";

        That pretty much covers the basics of hashes. Nothing to be afraid of, and quite a useful data type!

Re: Replacing values in an array
by BrowserUk (Pope) on Jan 26, 2013 at 23:43 UTC

    Somewhat simpler version:

    my( $n, %h ) = -1; my @a = map{ $h{$_} //= $n+=2 } qw[ M94202 M94150 M94297 M94150 M94161 + M94161 M94162 ];; print @a;; 1 3 5 3 7 7 9

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      Brilliant!
      Now I have three elegant solutions and a reason to study "map"!
      Thank you!
      -tonto

        map and grep can be an intimidating functions, but quite useful once you understand them.

        Let's consider grep first, since this one is a little simpler to grasp — or at least, that was my experience. grep takes two arguments, the second being a list of elements to work with, the first being the work that you want to have done on that list. You can specify that as a BLOCK, a code reference, or just the name of a function. grep then loops over the list, aliasing $_ to each element in turn, and calls the given piece of code. Then it returns every element for which the code returned a true value.

        my @numbers = (0, 0.5, 1, 1.5, 2, 2.5); # Calling grep with a BLOCK: my @integers = grep {int($_) == $_} @numbers; print join(", ", @integers), " are integers.\n"; # Calling grep with a function name: my @basket = ("apple", undef, undef, undef, "banana", "cherry", undef, + "date"); print "The number of elements in \@basket is ", scalar(@basket), "\n"; my @basket_1 = grep defined, @basket; print "The number of *defined* elements in \@basket is ", scalar(@bask +et_1), "\n";

        See what happens? grep returns the elements of the list you gave it, for which the piece of code returns true. The non-grep equivalents would be:

        my @numbers = (0, 0.5, 1, 1.5, 2, 2.5); # Calling grep with a BLOCK: my @integers; for (@numbers) { push @integers, $_ if int($_) == $_; } print join(", ", @integers), " are integers.\n"; # Calling grep with a function name: my @basket = ("apple", undef, undef, undef, "banana", "cherry", undef, + "date"); print "The number of elements in \@basket is ", scalar(@basket), "\n"; my @basket_1; for (@basket) { push @basket_1, $_ if defined; } print "The number of *defined* elements in \@basket is ", scalar(@bask +et_1), "\n";

        Now, map is pretty similar, except that it allows you to change the elements:

        my @numbers = 1..10; my @times_ten = map { $_ * 10 } @numbers; print join(", ", @times_ten), "\n";

        Of course, these are just the basics — the range of things you can do with them is astonishing. I hope this helps you along.

      Using for instead of map:

      use strict; use warnings; use Data::Dumper; my @a = ("M94202", "M94150", "M94297", "M94150", "M94161", "M94161", " +M94162"); my ( $n, %h ) = -1; $_ = $h{$_} //= $n+=2 for @a; print Dumper \@a;

      I suppose if you were playing golf, without use strict, you might write:

      my ( $n, %h ) = -1; $_ = $h{$_} //= $n+=2 for @a;
      in one-line as:
      $_=$h{$_}//=$}+=2-!$}for@a;
      (/me ducks)

Re: Replacing values in an array
by johngg (Abbot) on Jan 27, 2013 at 00:05 UTC

    A slight variation on toolic's and eyepopslikeamosquito's solutions in that I use a closure to generate the odd numbers (it returns the next odd number every time it is called) and I wrap the logic for finding duplicates in a do block to avoid leaving the %seen hash lying around after it is no longer needed.

    $ perl -Mstrict -Mwarnings -MData::Dumper -e ' > my $oddNos = do { > my $val = -1; > sub { $val += 2; }; > }; > > my @array = qw{ > M94202 > M94150 > M94297 > M94150 > M94161 > M94161 > M94162 > }; > > @array = do { > my %seen; > map { > exists $seen{ $_ } > ? $seen{ $_ } > : do { > $seen{ $_ } = $oddNos->(); > $seen{ $_ }; > }; > } @array; > }; > > print Data::Dumper->Dumpxs( [ \ @array ], [ qw{ *array } ] );' @array = ( 1, 3, 5, 3, 7, 7, 9 ); $

    I hope this is of interest.

    Cheers,

    JohnGG

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1015541]
Approved by toolic
Front-paged by 2teez
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others musing on the Monastery: (3)
As of 2014-04-21 02:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    April first is:







    Results (489 votes), past polls