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

Hi, pretty simple question here for the experts. I'm running a .csv list through a foreach loop, and if certain criteria is met, I want to print it out but separated by categories:

@data = <IN>; my @sorted_data = sort @data; foreach $line (@sorted_data) { chomp $line; $line =~ /(.*?)\,(.*?)\,(.*)/; $first = $1; $second = $2; $last = $3; if ($last eq $second) { if($second eq $first) { $print "$second really = $first\n"; } else { print "$last only = $first\n"; } } }

Instead of doing the print line by line, I'd like to get out of the foreach to print everything separately - like all the $second = $first together and the $last = $first together. Current output has them all mixed up since I'm looking at it line by line and printing the same. Would hash be better? sub? Would appreciate any assistance.

Replies are listed 'Best First'.
Re: Printing Array Output Outisde foreach
by tangent (Parson) on Mar 07, 2014 at 15:51 UTC
    You could use arrays to store your results. Note where you say print "$last only = $first\n" you haven't tested if last eq first, only if last eq second.
    my (@both,@last_second,@last_first); foreach $line (@sorted_data) { chomp $line; $line =~ /(.*?)\,(.*?)\,(.*)/; $first = $1; $second = $2; $last = $3; if ($last eq $second and $second eq $first) { push(@both,"$last = $second and $second = $first"); } elsif ($last eq $second) { push(@last_second,"$last = $second"); } elsif ($last eq $first) { push(@last_first,"$last = $first"); } } print "both\n", join("\n",@both), "\n"; print "last_second\n", join("\n",@last_second), "\n"; print "last_first\n", join("\n",@last_first), "\n";
Re: Printing Array Output Outisde foreach
by kcott (Archbishop) on Mar 07, 2014 at 23:34 UTC

    G'day ahjohnson2,

    As 2teez indicated, you really should be using Text::CSV, or one of its related modules. Can you guarantee that none of your data (from now until the end of time) will ever contain a comma or have any other issue related to parsing CSV files?

    You appear to have a logic error in the code you posted. If $last eq $second is TRUE and $second eq $first is FALSE, then "$last only = $first\n" will not be a true statement; however, "$last only = $second\n" would be a true statement. Also, in "$second really = $first\n", all three variables would be equal. I'm somewhat lost as to what you're actually attempting to report with this code.

    In order to group the printing of the results of your conditions, you could save the tested data in an AoAoA (see "perldsc - Perl Data Structures Cookbook" if that's unfamiliar to you) which looks like this:

    ( [ # data matching first condition [ $first, $second, $third ], [ $first, $second, $third ], ..., ], [ # data matching second condition [ $first, $second, $third ], [ $first, $second, $third ], ..., ], )

    You could then set up another array with rules on how to deal with the data matching each of those conditions.

    This script is intended to show how to group the printing. I've shown a fairly simple set of rules as well as a more complex set. I'll leave parsing the CSV data and fixing the logic errors to you.

    #!/usr/bin/env perl -l use strict; use warnings; my @print_data; for (sort <DATA>) { chomp; my ($first, $second, $third) = split /,/; next if $second ne $third; push @{$print_data[$first ne $second]}, [ $first, $second, $third +]; } { print '*** Simple Formatting Rules ***'; my @rules = ( "%s (1st) equals %s (2nd) and %2\$s (2nd) equals %s (3rd)\n", "%s (1st) doesn't equal %s (2nd) but %2\$s (2nd) does equal %s + (3rd)\n", ); for my $i (0 .. 1) { printf $rules[$i], @$_ for @{$print_data[$i]}; } } { print '*** More Complex Formatting Rules ***'; my @rules = ( [ "%s (3rd) equals both %s (1st) and %s (2nd)\n", [ 2, 0, 1 +], ], [ "%s (3rd) only equals %s (2nd)\n", [ 2, 1 ], ], ); for my $i (0 .. 1) { printf $rules[$i][0], @{$_}[@{$rules[$i][1]}] for @{$print_dat +a[$i]}; } } __DATA__ 2,2,3 2,2,2 2,1,1 1,2,3 1,2,2 1,1,1

    Output:

    *** Simple Formatting Rules *** 1 (1st) equals 1 (2nd) and 1 (2nd) equals 1 (3rd) 2 (1st) equals 2 (2nd) and 2 (2nd) equals 2 (3rd) 1 (1st) doesn't equal 2 (2nd) but 2 (2nd) does equal 2 (3rd) 2 (1st) doesn't equal 1 (2nd) but 1 (2nd) does equal 1 (3rd) *** More Complex Formatting Rules *** 1 (3rd) equals both 1 (1st) and 1 (2nd) 2 (3rd) equals both 2 (1st) and 2 (2nd) 2 (3rd) only equals 2 (2nd) 1 (3rd) only equals 1 (2nd)

    -- Ken

Re: Printing Array Output Outisde foreach
by hippo (Archbishop) on Mar 07, 2014 at 15:44 UTC

    Untested, but you get the idea:

    my $lastreport = ''; foreach $line (@sorted_data) { chomp $line; $line =~ /(.*?)\,(.*?)\,(.*)/; $first = $1; $second = $2; $last = $3; if ($last eq $second) { if($second eq $first) { print "$second really = $first\n"; } else { $lastreport .= "$last only = $first\n"; } } } print $lastreport;
Re: Printing Array Output Outisde foreach
by 2teez (Vicar) on Mar 07, 2014 at 16:00 UTC

    Hi ahjohnson2,
    ..I'm running a .csv list through a foreach loop, and if certain criteria is met, I want to print it out but separated by categories..

    Since, you have a CSV file, why not use Text::CSV or Text::CSV_XS
    And since, a sample of your dataset is not shown, what categories are you talking about please?

    Then have you also tried using Data::Dumper to print your final output to see how it might look like?

    If you tell me, I'll forget.
    If you show me, I'll remember.
    if you involve me, I'll understand.
    --- Author unknown to me