http://www.perlmonks.org?node_id=1039590

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

Hello, I've been searching for a very long time how to do the following:

I have an array, containing the elements (for example):

200 201 205 194 140 250 280

I want to substitute all values that are greater than 200 for the number 200, so ideally the new or modified array would contain:

200 200 200 194 140 200 200

My attempt to do this has involved using s///, tr///, etc. etc. Any suggestions?

my code:

@array = ('200', '201', '205', '194', '140', '250', '280'); print "original\n" , "@array " , "\n"; $count = @array; for ($i = 0; $i > $count-1; $i++) { if ($array[$i] gt '200') { s/$array[$i]/200/; } } print "new:\n" , "@array ", "\n";

which returns:

original 200 201 205 194 140 250 280 new 200 201 205 194 140 250 280

Replies are listed 'Best First'.
Re: substituting values in an array
by davido (Cardinal) on Jun 18, 2013 at 16:41 UTC

    You are treating numbers as strings. Is it intentional that you want stringy-semantics on your comparisons? Regardless, the standard way to transform an array is via map. This isn't a job for tr, or for s///. If I understand your intent, you want to take an array of numbers, and constrain it such the all elements are 200 or less. Any element over 200 becomes 200.

    my @array = ( 200, 201, 205, 194, 140, 250, 280 ); my @constrained = map { $_ > 200 ? 200 : $_ } @array; print "@array\n";

    I understand you've been searching a long time on how to do this. Sometimes better than searching is asking yourself, "How would I do this on paper?" If I were doing it on paper, I would look at an element, and then make the decision, "Is the element greater than 200?" If it is, write down 200. If not, write down the original element. Great. Now we know how we would do it on paper; let's put it to code... and that produces what you see above.


    Dave

Re: substituting values in an array
by talexb (Chancellor) on Jun 18, 2013 at 16:38 UTC

    Here's some code that worked for me:

    #!/usr/bin/perl use strict; use warnings; { my @arr = qw/200 201 205 194 140 250 280/; print "Original array is " . join(', ',@arr) . "\n"; @arr = map { $_ > 200 ? 200 : $_ } @arr; print "Limited array is " . join(', ',@arr) . "\n"; }

    Alex / talexb / Toronto

    "Groklaw is the open-source mentality applied to legal research" ~ Linus Torvalds

Re: substituting values in an array
by tobyink (Canon) on Jun 18, 2013 at 17:00 UTC
    ($_ > 200) && ($_ = 200) for @array;
    package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name

      Take notice of what Toby suggests here.   Notice that there is no indexing going on, and that a change to a scalar variable (the built-in $_ in this case) effects a change in the value of the array element.   The reason is that it is a reference to that element.   The variable walks down the entire length of the array, and, because it is a reference to each value in turn, and not merely a copy of it, it can change it.

      The somewhat “golf”-ish use of && is a “short-circuit” boolean-AND.   If the expression on the left-hand side is True, the expression on the right-hand side is evaluated (and does something); otherwise it is not evaluated at all (since False AND anything is known to always be False).

      This kind of “brevity of expression” is very common in Perl.

        In this case, the golf is not just for leisure. You'd have to dig out the XS to write a faster solution. On a large array, it's about twice as fast as using map, and about 50% faster than using (admittedly somewhat more readable) for and if blocks.

        Also quite fast (though just a teeny bit slower) would be:

        $_ = ($_ > 200) ? 200 : $_ for @array;

        ... though that might degrade quite badly on a tied array as it performs unnecessary extra FETCH and STORE operations.

        package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
Re: substituting values in an array
by toolic (Bishop) on Jun 18, 2013 at 16:42 UTC
    You have 2 problems: wrong end condition in your for loop and you are trying to substitute from the unassigned $_ variable. No need for s/// ... just assign.
    @array = ('200', '201', '205', '194', '140', '250', '280'); print "original\n" , "@array " , "\n"; $count = @array; for ($i = 0; $i < $count; $i++) { if ($array[$i] gt '200') { $array[$i] = 200; } } print "new:\n" , "@array ", "\n";

    Tip #2 from the Basic debugging checklist: print

Re: substituting values in an array
by Laurent_R (Canon) on Jun 18, 2013 at 19:01 UTC

    if ($array[$i] gt '200') { # ...

    Good solutions have been provided to you, I would just like to point out to a severe error in your code that has not been picked up so far if I have read correctly.

    If you want to compare numbers numerically (I mean arithmetical comparison), don't use the gt operator. If your array contains the element 1500, this will be deemed to be smaller than 200, because gt makes some form of lexical (or lexicographical, whatever) comparison (and 1 is smaller than 2). This is obviously not what you want. In brief, in such a case you want to use the > operator, rather than gt.