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

We have an input file which looks something like this

lastname,firstname,age,height,weight

1lastname,1firstname,1age,1height,1weight

I need to know how to split the last names into an array and the first names in to another array and so forth so that I can sort them seperately. Can anyone help???

Replies are listed 'Best First'.
Re: array splitting and sorting
by bikeNomad (Priest) on Jun 01, 2001 at 08:43 UTC
    Do you really need to split the lines into separate arrays to sort them? That is, do you want a separate sorted list of lastnames, a sorted list of firstnames, and so on? Or do you want to sort the lines by lastname and then firstname?

    If you want the latter (I'm guessing you do), all you have to do is to make a custom sort routine. You can either leave the lines as they are (to save on memory), or split them (to save time, perhaps, and to make it easier to deal with the data later). The easiest to demonstrate is probably to split them first:

    my @lines; while (<>) { chomp; my @line = split(','); push(@lines, \@line); } @lines = sort { $a->[0] cmp $b->[0] or $a->[1] cmp $b->[1] or $a->[2] <=> $b->[2] or $a->[3] <=> $b->[3] or $a->[4] <=> $b->[4] } @lines; foreach my $line (@lines) { print join(",", @{$line}) . "\n"; }
    Here, we read each line, strip off line endings, and then split it on comma boundaries. A reference to the resultant array is then pushed into the array @lines. Then we sort @lines using a custom sort routine that does a sort by the fields in order. This assumes that the first two fields are alphabetical, and the rest numeric. I'm assuming that height is in some rational form like cm.

    Then the program just prints out the sorted lines, with commas put back in. You can fiddle with the sort routine to make it case insensitive, etc. but the basic idea still stands.

      Thanks for your wisdom.

      I tried this:

      while (<FILE>){ #FILE=filehandle chomp; (@last, @first, @age, @sex, @height, @weight, @Comment) = split (/\,/) +; }

      however it did not work. I am assuming that the reason it did not work was cause I was using the string split, and tring to convert it to spliting an array.

      Thanks again for your help. I will try this out.

      A greatfull, Traineeeee.....

        Your problem is that the while iterates over each record in turn (setting it to the magical $_), thus you need to set each element of each array in turn, this is done by modifying your code thusly:

        $i = -1; while (<FILE>){ chomp; $i++; ($last[$i], $first[$i], $age[$i], $sex[$i], $height[$i], $weight[$i +]) = split/,/; } die "Sorry, no data\n" if $i == -1;

        Note you do not need the \ before the comma in your split or the parenths. Presume you have no problems with the sort, if not you can use:

        @alphabetical = sort @alphabetical # the above is equivalent to @alphabetical = sort {$a cmp $b} @alphabetical @numerical = sort {$a <=> $b} @numerical; # to reverse the sort order reverse the positions of $a and $b, or use + reverse sort @a

        Hope this helps

        Cheers

        tachyon

(jeffa) Re: array splitting and sorting
by jeffa (Bishop) on Jun 01, 2001 at 20:33 UTC
    Eek! I can't believe that no one metioned DBD::CSV. I see two answers that are using different arrays to store the data - why not just use a 2-d array?
    my (%fields,@records); @fields{qw(lastname firstname age height weight)} = (0..4); while(<DATA>) { chomp; my @row = split(/,/,$_); push @records, \@row; } __DATA__ Smith,Joe,40,76,175 Page,Betty,60,58,165 Nader,Ralph,55,63,190 Alexander,Sue,25,54,98 Saxon,Jackson,32,60,155
    Now you can write a subroutine that will sort the 2-d array and you can use %fields to reference the field by a name rather than a number.

    But your subroutine should be smart enought to know the when to do a numerical sort ({ $a <=> $b }) and when to do an 'ASCII' sort ( { $a cmp $b }). It should also be smart enought to sort ascending ($a before $b) and descending ($b before $a).

    You could do that, or you could use DBD::CSV:

    use DBI; use Text::CSV_XS; # or your other favorite CSV module my $CSV = new Text::CSV_XS; my $DBH = DBI->connect("DBI:CSV:"); $DBH->{RaiseError} = 1; $DBH->do(" create table info( lname char(20), fname char(20), age integer, height integer, weight integer ) "); # i am still new to DBD::CSV, i am sure there is a way # to eliminate this while loop, i'll be researching... # in the meantime, we can use Text::CSV_XS . . . while (<DATA>) { chomp; $CSV->parse($_) or next; my $sth = $DBH->prepare(" insert into info values(?,?,?,?,?) "); $sth->execute($CSV->fields); } my $sth = $DBH->selectall_arrayref(" select * from info order by lname desc, fname asc "); foreach (@$sth) { print join("\t", @$_), "\n"; } # very mandatory to drop the table explicitly # if you wish to run this program more than once $DBH->do("drop table info"); __DATA__ Smith,Joe,40,76,175 Page,Betty,60,58,165 Nader,Ralph,55,63,190 Alexander,Sue,25,54,98 Saxon,Jackson,32,60,155
    this will produce:
    Smith Jane 30 57 113 Smith Joe 40 76 175 Saxon Jackson 32 60 155 Page Betty 60 58 165 Nader Ralph 55 63 190 Alexander Sue 25 54 98

    Jeff

    R-R-R--R-R-R--R-R-R--R-R-R--R-R-R--
    L-L--L-L--L-L--L-L--L-L--L-L--L-L--
    
Re: array splitting and sorting
by runrig (Abbot) on Jun 01, 2001 at 18:40 UTC
    I'm thinking that maybe you just want hash arrays? Do you really want to keep duplicates in the array?
    my (%last_names, %first_names); while (<>) { my ($last, $first) = split /,/; # Instead of undef, you could use '++' if you want to # count names e.g. # $last_names{$last}++; # or treat as an array and push the data '$_' to the hash, # e.g. # push @{$last_names{$last}}, $_; # Or even create a hash of hashes, etc. $last_names{$last} = undef; $first_names{$first} = undef; } print "$_\n" for sort keys %last_names; #etc.
Re: array splitting and sorting
by buckaduck (Chaplain) on Jun 01, 2001 at 21:49 UTC
    Or the module Data::Table. If the CSV file is similar to the one jeffa used earlier:
    lastname,firstname,num1,num2,num3 Smith,Joe,40,76,175 Page,Betty,60,58,165 Nader,Ralph,55,63,190 Alexander,Sue,25,54,98 Saxon,Jackson,32,60,155
    Then you can use this code to generate a Data::Table object. This example prints it out as an HTML table, but there are other options...
    use Data::Table; # Create a table object from a CSV file my $table = Data::Table::fromCSV("data.txt"); # Sort by last name, then by first name $table->sort('lastname', 0, 0, 'firstname', 0, 0); # Print it as an HTML table, for example print $table->html;
    buckaduck
Re: array splitting and sorting
by diarmuid (Beadle) on Jun 01, 2001 at 18:25 UTC
    well it's pretty straight forward...
    my(@last_name,@first_name,@age,@height,@weight); my $i=0; while(<INPUT>){ ($last_name[$i],$first_name[$i],$age[$i],$height[$i],$weight[$i] = s +plit(/,/); $i++; }