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

Hey! I really need help- no one seems to know... I parse a flat file that looks like this: report: sort source-a,port-a,time-a Monmouth,2000-05-2000,192,53 Jackson,2000-04-2100,167,24 Monmouth,2000-04-1000,63,12 the report line tells me that it's a sort on these fields with -a (ascending) or -d (descending). The sort conditions are inputed by the user so it changes. I need to print out the the entire line of the file not just the inputed fields by the user. So, it should sort on source in asc. order first. If there are any matches then sort on port in asc. then if there are any matches then the last field. I can split the data, call the sort subroutine but I can't do the multiple sort it. Anything is apprecitated! PMONK

Replies are listed 'Best First'.
Re: Multiple Sorts
by blakem (Monsignor) on Sep 09, 2001 at 09:09 UTC
    Take a look at the FMTEYEWTK article on sort (Far More Than Everything You Every Wanted To Know) where two leading perl coders give answers to a very similiar question.

    -Blake

Re: Multiple Sorts
by tachyon (Chancellor) on Sep 09, 2001 at 13:51 UTC

    Here is how you adapt a Schwartzian transform to sort multiple fields - this sorts on field 1 first then field 2 then 3. Sorting on multiple fields is explained in the perlman:perlfunc under sort.

    @list = <DATA>; @sorted = map { $_->[0] } sort { $a->[1] cmp $b->[1] || $a->[2] cmp $b->[2] || $a->[3] cmp $b->[3] } map { [ $_, split/\|/ ] } @list; print @sorted; __DATA__ C|A|A C|B|A C|C|A B|A|A B|B|A B|C|A A|A|A A|B|A A|C|A C|A|B C|B|B C|C|B B|A|B B|B|B B|C|B A|A|B A|B|B A|C|B C|A|C C|B|C C|C|C B|A|C B|B|C B|C|C A|A|C A|B|C A|C|C

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: Multiple Sorts
by I0 (Priest) on Sep 09, 2001 at 11:50 UTC
    How do you split the data and call the sort subroutine?
      I dont' think I'm splitting the data right. I don't think I should push the data but I don't know how else to do it. Here is my main program with the test.dat (data file) below. I really need help this is due tomorrow! ################################
      #sort.pl #!/usr/bin/perl #use IO::Socket; my $datafile = "test.dat"; open fh, $datafile or die "Can't open $datafile: $!"; my %report; # for report info my %list; while (<fh>) { # skip blank and commented lines next if /^\s*#/; next if /^\s*$/; # We'll look for lines that describe a report: if (s/^\s*report:\s+//i) { # line is a report description chomp; @report{qw(sort list)} = split; # set defaults $report{sort} ||= 'source'; $report{acl} ||= 'all'; } else { my ($log_source,$time,$source_ip,$source_port,$dest_ip,$dest_port, $repeats,$acl_number,$log_number) = (split/,/); push @{$list($_)},(site => $log_source,time =>$time, sip=>$source_ip, sport =>$source_port, dip => $dest_ip, dport =>$dest_port, hits =>$repeats, acl =>$acl_number, lnum =>$log_number); } close fh; if ( $report{acl} eq 'all' ) { for ( sort keys %list ) { list_by_sort($_,$_,$_,$_, \%list) if $report{sort} eq 'sort'; } } else { list_by_sort($report{list}, \%list) if $report{sort} eq 'sor +t'; } sub list_by_sort($_,$_,$_,$_,\%list) { #input from the report line of the data my $field1 = shift; my $field2 = shift; my $field3 = shift; my $field4 = shift; my $date = localtime; $date =~ s/ /-/g; my $report_file_name = "$date.SORT"; ###stores report data in file open OF, ">$report_file_name" or die "Can't Open $report_file_ +name: $!"; print OF " REQUEST FOR SORTING\n\n"; print OF "FILE WAS GENERATED ON: $date \n"; #know I don't know how to compare the fields and do the sorts #the -a is for asc. and -d for descending } test.dat--The data looks like this: report: sort site-a,time-a,sport-d,hits-a Monmouth,2000-05-2000:00:09-04,192.35.75.69,138,192.100.255,66,2,101,1 +234 Jackson,2000-04-2100:00:10-05,192.35.12.03,144,192.67.29,134,8,101,148 +7 Meade,2001-01-0500:00:11-04,213.132.32,175,184.57.62.35,151,12,101,153 +2
        #so it sounds like your asking for something like this?
        while (<DATA>) { # skip blank and commented lines next if /^\s*#/; next if /^\s*$/; # We'll look for lines that describe a report: if (s/^\s*report:\s+//i) { # line is a report description chomp; @report{qw(sort list)} = split; # set defaults $report{sort} ||= 'source'; # $report{acl} ||= 'all'; } else { my ($log_source,$time,$source_ip,$source_port,$dest_ip,$dest_port, $repeats,$acl_number,$log_number) = (split/,/); $list{$_}={site => $log_source,time =>$time, sip=>$source_ip, sport =>$source_port, dip => $dest_ip, dport =>$dest_port, hits =>$repeats, acl =>$acl_number, lnum =>$log_number}; } } #close fh; if ( $report{acl} eq 'all' ) { for( qw(site-a time-a sport-d hits-a) ){ list_by_sort($_, \%list) if $report{sort} eq 'sort'; print "\n" } } else { list_by_sort($report{list}, \%list) if $report{sort} eq 'sor +t'; } sub ST(&@){ my $metric=shift; map {$_->[0]} sort {$a->[1] cmp $b->[1]} map {[$_,&{$metric}]} @_ } sub list_by_sort{ my @fields = split/,/,shift; my $list = shift; #input from the report line of the data my $date = localtime; $date =~ s/ /-/g; my $report_file_name = "$date.SORT"; ###stores report data in file open OF, ">>$report_file_name" or die "Can't Open $report_file +_name: $!"; print OF " REQUEST FOR SORTING\n\n"; print OF "FILE WAS GENERATED ON: $date \n"; #know I don't know how to compare the fields and do the sorts #the -a is for asc. and -d for descending print OF ST{ my $key=$_; my %h = %{$list->{$key}}; join"\0",map{ my($k,$d)=split/-/; $d eq "d"?~$h{$k}:$h{$k}; }@fields; } keys %$list; } # __DATA__ report: sort site-a,time-a,sport-d,hits-a Monmouth,2000-05-2000:00:09-04,192.35.75.69,138,192.100.255,66,2,101,1 +234 Jackson,2000-04-2100:00:10-05,192.35.12.03,144,192.67.29,134,8,101,148 +7 Meade,2001-01-0500:00:11-04,213.132.32,175,184.57.62.35,151,12,101,153 +2