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

I know this would be fairly easy, but Im a programming newbie. How would you condense this code using a loop in perl? Thanks in advance for ur help.
if ($total_nums <= 30) { $test_nums = 1; } if ($total_nums >= 31 and $total_nums <= 60) { $test_nums = 2; } if ($total_nums >= 61 and $total_nums <= 90) { $test_nums = 3; } if ($total_nums >= 91 and $total_nums <= 120) { $test_nums = 4; } if ($total_nums >= 121 and $total_nums <= 150) { $test_nums = 5; } if ($total_nums >= 151 and $total_nums <= 180) { $test_nums = 6; }

Replies are listed 'Best First'.
Re: condensing code
by chromatic (Archbishop) on Sep 08, 2006 at 17:48 UTC

    I'd divide $total_nums by 30 and check the remainder.

Re: condensing code
by jdtoronto (Prior) on Sep 08, 2006 at 17:50 UTC
    Look at POSIX specifically floor and ceil and at int, those plus a simple division should see you right.

    jdtorono

Re: condensing code
by merlyn (Sage) on Sep 08, 2006 at 21:21 UTC
Re: condensing code
by ptum (Priest) on Sep 08, 2006 at 17:50 UTC

    Not being entirely facetious, but sometimes the best condensation of an if/else clause is not to use one at all, as this rather silly example indicates.

    $test_nums = int(($total_nums/30) - .01) + 1;
Re: condensing code
by diotalevi (Canon) on Sep 08, 2006 at 17:54 UTC

    Here's two thoughts. elsif and a dispatch table were other thoughts.

    # A ternary $test_nums = ( $total_nums <= 30 ? 1 : $total_nums <= 60 ? 2 : $total_nums <= 90 ? 3 : $total_nums <= 120 ? 4 : $total_nums <= 150 ? 5 : $total_nums <= 180 ? 6 : undef ); # Simple division use POSIX 'ceil'; $test_nums = ceil( $total_nums / 30 );

    ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

Re: condensing code
by friedo (Prior) on Sep 08, 2006 at 17:51 UTC
    I'm making a big assumption about your data, but it looks like you just want $test_nums = int( $total_nums / 30 ) + 1;

      That was my initial thought but that would make 30 => 2 and not 1. So I think he wants int( ($num-1) / 30 ) + 1.

      #!/usr/bin/perl use strict; use warnings; my @nums = ( 1 .. 1000 ); foreach my $num ( @nums ) { my $test_n = int( ($num-1) / 30 ) + 1; print "Num: $num - $test_n \n"; }

      -derby

        Um, technically this won't work either for $total_nums = 1, much as friedo's version doesn't work for $total_nums values of 30, 60, 90, etc., based on the OP's original code. Looks like diotalevi's example using ceil() is better.

        Update: Ah, I see you updated your version to add 1 after getting the int() value. Fine.

        (Blush)Another Update: Uh, actually, not fine. Now yours doesn't work for values of 31, 61, etc.

Re: condensing code
by jZed (Prior) on Sep 08, 2006 at 17:47 UTC
    One way:
    $test_nums = ($total_nums <= 30) ? 1 : ($total_nums >= 31 and $total_nums <= 60) ? 2 : ($total_nums >= 61 and $total_nums <= 90) ? 3 : ($total_nums >= 91 and $total_nums <= 120) ? 4 : ($total_nums >= 121 and $total_nums <= 150) ? 5 : ($total_nums >= 151 and $total_nums <= 180) ? 6;
      ( Note: I assumed $total_nums contains an integer or that your code was mishandling 30.5 and the like. )

      If you invert the order of the checks, you can simplify to the following.

      $test_nums = $total_nums > 180 ? $test_nums : $total_nums > 150 ? 6 : $total_nums > 120 ? 5 : $total_nums > 90 ? 4 : $total_nums > 60 ? 3 : $total_nums > 30 ? 2 : 1;

      Since the breakpoints form continuous serious of multiples of 30, the above could be simplified further using a loop. Or better yet, using division.

      if ( $total_nums <= 0 ) { $test_nums = 1; } elsif ( $total_nums <= 180 ) { $test_nums = int( ( $total_nums - 1 ) / 30 ) + 1; }

      Update: I got sidetracked while writting my reply, so other people have posted similar solutions already. Please excuse any duplication.

      How would I write it so that if $total_nums = 1000; I wont have to write all that code and it will loop through all the numbers and be able to give back the number I need?? Thanks for your input.
Re: condensing code
by BrowserUk (Patriarch) on Sep 08, 2006 at 17:55 UTC

    Something like this?

    for $total_nums ( map $_*5, 0 .. 40 ) { printf "$total_nums =>"; $test_nums = 1; $test_nums++ while ( $total_nums -= 30 ) > 0; print $test_nums; };; 0 =>1 5 =>1 10 =>1 15 =>1 20 =>1 25 =>1 30 =>1 35 =>2 40 =>2 45 =>2 50 =>2 55 =>2 60 =>2 65 =>3 70 =>3 75 =>3 80 =>3 85 =>3 90 =>3 95 =>4 100 =>4 105 =>4 110 =>4 115 =>4 120 =>4 125 =>5 130 =>5 135 =>5 140 =>5 145 =>5 150 =>5 155 =>6 160 =>6 165 =>6 170 =>6 175 =>6 180 =>6 185 =>7 190 =>7 195 =>7 200 =>7

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: condensing code
by ady (Deacon) on Sep 08, 2006 at 18:08 UTC
    And a C-way to do it..
    #!/usr/bin/perl -w use strict; my $t= -181; for (my ($i,$j) = (30,1); $i <= 180; $i += 30, $j++) { if ($t <= $i) { print $j; last; } }

    The div by 30 is much more straight forward tho'!
Re: condensing code
by Anonymous Monk on Sep 08, 2006 at 18:08 UTC
    On the same note, how would I incorporate that in with this code to extract the data based on the number of elements in an array? I need to create 6 arrays(for example) from 1 array based on when $test_num = 6; Thanks again everyone...
    foreach $dist_flds (@all_st) { if ($counts < 30) { my $news = $dist_flds.","; #print "1 $news \n "; push(@store_get1,$news); } if ($counts >= 31 and $counts <= 60) { my $news = $dist_flds.","; #print "2 $news \n"; push(@store_get2,$news); } if ($counts >= 61 and $counts <= 90) { my $news = $dist_flds.","; #print "3 $news \n"; push(@store_get3,$news); } etc.........
      You want to split up array @all_st into subgroups of 30 each, I think. You can easily do this using the module List::MoreUtils' function natatime.

      You are just a beginner, so you probably aren't aware that, instead of creating 3 or however many separate subarrays of @all_st, you can create an array of arrays like. . . $store_get2[0][0]. This notation would access the first item in the first 'subarray'.

      You should read perllol which will explain this better than me :-)

      (You would probably be better off not appending a comma to your data at this stage of your program. It could more easily done later with a join ",", @some_list).

      Here is an example of using natatime

      #!/usr/bin/perl use strict; use warnings; use List::MoreUtils qw/ natatime /; use Data::Dumper; my @all_st = 1..125; my @store_get; my $it = natatime 30, @all_st; while (my @vals = $it->()) { push @store_get, [@vals]; } print Dumper \@store_get; __END__ C:\perlp>perl t3.pl $VAR1 = [ [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 ], [ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 ], [ 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90 ], [ 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120 ], [ 121, 122, 123, 124, 125 ] ]; C:\perlp>
      Update A better solution that uses no module and doesn't keep 2 sets of data when only one set is probably required. Also, it puts the data into a form that can be used for an *in* clause for a SQL select statement (single quotes enclosing the items for the *in* clause).
      #!/usr/bin/perl use strict; use warnings; my @all_st = 1..125; my @store_get; while (@all_st) { push @store_get, join ",", map "'$_'", splice @all_st, 0, 30; }
      Update: The in keyword will accept unquoted data if the type is integer. So the line above doesn't need the quotes added

      push @store_get, join ",", splice @all_st, 0, 30;
        Awesome thanks... Now how would I access the 1st, 2nd or 3rd set of data from that array? Thanks again.
      $count is the number of elements in the array.
Re: condensing code
by Anonymous Monk on Sep 08, 2006 at 17:39 UTC
    Oh I forgot, $total_nums = 250;