CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Intermediate:
Sort text file by field

 



Grizz
Novice

Jun 20, 2002, 2:11 PM

Post #1 of 7 (1302 views)
Sort text file by field Can't Post

Hi all,
I have a text file that looks something like this:
id|name|date|price|inv
I can sort it by putting it in an array and using sort which would sort by the id field, since it is first.. But if I want to sort by date, is there a quick way to do this other than pulling the line apart and moving that field to the front and then back again after sort?
Thanks


Grizz
Novice

Jun 20, 2002, 2:38 PM

Post #2 of 7 (1300 views)
Re: [Grizz] Sort text file by field [In reply to] Can't Post

I should clarify here after searching old posts and finding some info on this. I'll want to search on three different fields. The first would be a name field which should be a simple alphabetical search. The other fields would be a price field and inventory which will have different lengths of numbers which I would like to sort by value.
Thanks again


rGeoffrey
User / Moderator

Jun 20, 2002, 5:41 PM

Post #3 of 7 (1297 views)
Re: [Grizz] Sort text file by field [In reply to] Can't Post

I am going to guess that you want a function that you can call where you can tell it which field to sort on and it has to be able to any of them. If that is the case we can steal from the previous thread [url=http://www.perlguru.com/gforum.cgi?post=7454]print and sort. Some changes to that code gives us...


Code
#id|name|date|price|inv 
my @array = ('0|tom|2002/03/31|12.95|123',
'1|tim|2002/03/30|14.95|1234',
'2|mary|2001/12/01|12.94|456',
'3|tina|2004/12/01|99.99|4567',
'4|frank|2002/06/07|1000|789',
'5|michael|2001/11/11|999.99|1');

print ("\nThe original array is:<pre>\n ",
join ("\n ", @array),
"</pre>\n",
);

foreach my $key (qw (name date price)) {
print ("\n<h3>The sorted table for ('$key')is:\n</h3><table>\n",
'<tr>',
(map {"<th>$_</th>\n"} split ('\|', 'id|name|date|price|inv')),
"</tr>\n"
);

foreach my $row (&transform_sort ($key, @array)) {
my @cols = split ('\|', $row);
print ('<tr>',
(map {"<td>$_</td>\n"} @cols),
"</tr>\n");
}
print ("</table>\n");
}

sub transform_sort {
my $option = shift;

#id|name|date|price|inv
my %index = (id => [0, 1],
name => [1, 0],
date => [2, 0],
price => [3, 1],
inv => [4, 1]);

if ($index{$option}[1]) {
return (map { (split ('<->', $_))[1] }
sort { $a <=> $b }
map { join ('<->', lc ((split ('\|', $_))[$index{$option}[0]]), $_) }
@_
);
} else {
return (map { (split ('<->', $_))[1] }
sort
map { join ('<->', lc ((split ('\|', $_))[$index{$option}[0]]), $_) }
@_
);
}
}


Look at the original thread for an explanation of what is happening with the maps. The big differences in this version are:

1. transform_sort no longer needs $folder as you have simpler data. It takes a scalar to say which field to sort on and the then the array to sort.
2. %index now holds two values for each column. The first is still the index we had before, it tells the lower map which part to pull out for sorting. The second is a flag to say if we can do a normal asciibetical sort or must do a numeric sort.

Another assumption is that your date field will be YYYY/MM/DD which can be easily sorted as the fastest changing parts are on the right. If this is not the case you will need a third return statement from transform_sort where you put the date into a form that is easier to work with. Or it might be easier to change the data to match the program.


uri
Thaumaturge

Jun 20, 2002, 7:40 PM

Post #4 of 7 (1296 views)
Re: [rGeoffrey] Sort text file by field [In reply to] Can't Post

first off, i wouldn't print html in the output when it is not needed. running this script on the command line is much easier than installing it under a web server.

also your code is way more complex than needed. the map/sort/map idiom is well known and it is used in the schwartzian transform and the GRT. see my paper on perl sorting at:
http://www.sysarch.com/perl/sort_paper.html

the code you show is way more complex than is needed. in general you can create a sort routine for each type of sorting you need and that is much simpler than a general sort which can take field names and types. you then just select which sort routine to use via a dispatch table or similar method.


Grizz
Novice

Jun 21, 2002, 12:50 PM

Post #5 of 7 (1288 views)
Re: [uri] Sort text file by field [In reply to] Can't Post

Thanks for the replies.
After posting I did a search of the old posts(duh!! I know I'm supposed to do that first) and was able to figure this little bit of code out and it works.

Code
open (DATA,"$datpath") || print "$!"; 
my @data;
my @myArray;
my @newArray;
for(<DATA>){
my @fields = split/\|/;
unshift @data, \@fields;
}
close(DATA);
my @sorted = sort{$b->[4] <=> $a->[4]} @data;
for(@sorted){
my @fields = @$_;

my $line = join("|", @fields[0..4]);
if($line ne ""){
push(@myArray,$line);
}
}
## @fields array is in reverse order here for some reason so lets flip it
## for some reason unshift didn't work in previous statement

foreach $i(@myArray){unshift(@newArray,$i)}

## @newArray = sorted fields

Just have to change the field number and <=> to cmp, depending on what I need to sort.
This was adapted from a previous post by Abstract, so thanks.
Thanks to those who posted.
Chuck


BrightNail
Novice

Jun 21, 2002, 2:00 PM

Post #6 of 7 (1283 views)
Re: [Grizz] Sort text file by field [In reply to] Can't Post

Grizz,

can you go over you code for me....there are some things in there that I don't understand.....most notably..

my @fields = split/\|/;
unshift @data, \@fields;
}
close(DATA);
my @sorted = sort{$b->[4] <=> $a->[4]} @data;

fill me in..thanks

In Reply To


Grizz
Novice

Jun 22, 2002, 2:14 AM

Post #7 of 7 (1281 views)
Re: [BrightNail] Sort text file by field [In reply to] Can't Post

Well I'll tell you what I know. As I said the main part of this code was taken from another post and I changed a few things to make it work for me.

Code
open (DATA,"$datpath") || print "$!";  
my @data;
my @myArray;
my @newArray;
for(<DATA>){

# since this is a pipe delimited file, we split the fields on the "|". Your file could be seperating fields with
# tabs, commas or whatever.

my @fields = split/\|/;

# here we are taking the split fields that we just put in @fields and putting them in @data
#..unshift adds them to the top of the list
# where push would add to the end. Hmm! maybe thats why the list was upside down?

unshift @data, \@fields;
}
close(DATA);

# this is sorting field[4] and putting in @sorted. comparing two values at a time ($a and $b)

my @sorted = sort{$b->[4] <=> $a->[4]} @data;

# if you were sorting strings you would change this to:
# my @sorted = sort{lc($b->[4]) cmp lc($a->[4])} @data;

for(@sorted){

# this one I'm not exactly sure but I think its picking out our fields from @sorted. When I printed @sorted
# to the screen there was some extra fields in there, probably leftover from the sort

my @fields = @$_;

# now were putting our fields back together with the "|" Fields 0 to 4
# when I tried to print out seperate fields from @fields, I couldn't get them. Only output I could get was
# with $fields[0] So if this is an array, I couldn't get it to act like one. I used the next lines to get the fields
# into myArray

my $line = join("|", @fields[0..4]);

if($line ne ""){
push(@myArray,$line);
}
}
# At this point the sorted fields are sorted high to low so I used unshift to restack them in @newArray


Hope that helps.
Chuck

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives