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 flat database

 



mmcw
User

Jul 14, 2000, 6:39 AM

Post #1 of 4 (457 views)
Sort flat database Can't Post

I use the following to read the data from a file:

$datafile = "test.txt";

# Get all items
my $r_data = readdata($datafile);

#####
# subroutine readdata
# Sucks in all the data from $datafile
# and returns reference to the data
####

sub readdata {

my $datafile = shift;
my $r_data = [];

# Pull off datafile name and path
my ($datadir,$file) = pull_off($datafile);

if (open(DATA,"$datafile")) {
flock(DATA,2);
@$r_data = <DATA>;
flock(DATA,8);
close(DATA);
}
else {
my_die("Error in subroutine readdata: Can't open $datafile",$!);
}

$r_data;

}

The test.txt file contains the following:
The first row contains the field names.


ID|Name|Category|Title|Image|Description|Price|Taxable
0028|KINKS|10|Candy From Mr.Dandy.|geenidee.gif|label:<br>tracklist:<br>|30.00|1|
0029|LEED, Victor|11|Thanks Rock & Roll|geenidee.gif|rock&roll<br>label:<br>tracklist:|40.00|1|
0048|ALLIGATORS|0|Rockabillygator|geenidee.gif|ROCK&ROLL<br>label:<br>tracklist:|40.00|1|
0050|BISHOPS|1|Live!!!!!!!!!!!!!!!!!!!!!!!|geenidee.gif|label:<br>tracklist:<br>|40.00|1|
0051|CHURCH|2|Temperature Drop In Downtown|geenidee.gif|label:<br>tracklist:<br>|40.00|1|
0057|FLAMIN' GROOVIES|5|Sneakers|geenidee.gif|label:SKYDOG<br>tracklist:<br>golden clouds/the slide/prelude in a flat to/<br>afternoon of a pud/im drowning/babes in the sky/<br>love time/my yada.<br><br>|40.00|1|
0058|KINKS|10|Shape Of Things To Come|geenidee.gif|RAY DAVIES-related<br>label:<br>tracklist:|30.00|1|
0059|SQUEEZE|18|6 Squeeze Songs Crammed Into|geenidee.gif|label:<br>tracklist:<br>|25.00|1|


I can use @{$r_data} = sort @{$r_data};
The @{$r_data} will be sorted on ID!

But how to sort the @{$r_data} on:

1: ID
2: Name
3: Category
4: Title
5: Image
6: Description
7: Price
8: Taxable

greetings Michel



rGeoffrey
User / Moderator

Jul 14, 2000, 12:16 PM

Post #2 of 4 (457 views)
Re: Sort flat database [In reply to] Can't Post

First some things about a question you did not ask, is &readdata good...

<BLOCKQUOTE><font size="1" face="Arial,Helvetica,sans serif">code:</font><HR>


sub readdata {
my $datafile = shift;

open(DATA,$datafile) or my_die("Error in subroutine readdata: Can't open $datafile",$!);
flock(DATA,2);
my @data = <DATA>;
close(DATA);

return (\@data);
}
</pre><HR></BLOCKQUOTE>

You don't need to free the flock because closing the file will do that for you.

I have eliminated the if/else and used the more perlish open or die.

I switched to declaring @data rather than $r_data. I still return the pointer to the array, but you don't have to jump through extra hoops to read from the file.

And I removed the call to &pull_off. You assign the values to my variables so they are only good within this subroutine, yet you never use them. If you need this call, you should either skip any return value, or return it's return values to the place that calls &readdata, or assign them to globals (yuck).

Here is a main program complete with extra code to print each sorting so I could test it.

<BLOCKQUOTE><font size="1" face="Arial,Helvetica,sans serif">code:</font><HR>


$datafile = "test.txt";

# Get all items
my $r_data = readdata($datafile);

my $labels = shift @{$r_data};

my $ct;
my %where = map { $_, $ct++ } split ('\|', $labels);

foreach (keys (%where)) {
$r_data = &Special_Sort ($where{$_}, $r_data);
print ("----------------------------------------------$_\n",
join ("\n", @{$r_data}), "\n");
}
</pre><HR></BLOCKQUOTE>

&Special_Sort is given two things, the index of which field to sort on, and a pointer to the array to sort. It will return a pointer to an array. I would have set things up to take an array and return an array because that makes things work easier, but I will go with it because there is probably a good reason in this case.

To make choosing the index easier, I have %where which has its keys matching the fields, and the values being the correct index into the array after the split.

The foreach loop is just here for testing.

And now for the part we have all been waiting for...

<BLOCKQUOTE><font size="1" face="Arial,Helvetica,sans serif">code:</font><HR>


sub Special_Sort
{
my ($index, $r_data) = @_;

my @array =
map { (split ('<->', $_))[1] }
sort
map { join ('<->', lc ((split('\|', $_))[$index]) , $_) }
@{$r_data};

return (\@array)
}
</pre><HR></BLOCKQUOTE>

Note that the category in the third field will not sort well because I am using the normal sort which does things by string not by number. It would be easiest to pad the data so the normal sort will work.

We do a Schwartzian Transform in this function where we take the input array on the last line, feed it to map to create a more easily sorted array, feed that to sort, and feed that array to a second map on the top line that collects just the part we care about to return to the new array.

The important part is the first map which will find the correct data from each line that we will be doing the actual sort on. It then makes everything lower case to help sort work properly and the creates a new string with the thing to sort on, a separator '<->', and the actual string to return.

Then a simple sort on a set of long strings where we really only care about the part before the '<->'.

And finally the second map gets us back the part after the '<->'.


mmcw
User

Jul 15, 2000, 1:30 AM

Post #3 of 4 (457 views)
Re: Sort flat database [In reply to] Can't Post

Thank you,

I think I can get it to work.
But how to make it reverse sort?


mmcw
User

Jul 15, 2000, 4:16 AM

Post #4 of 4 (457 views)
Re: Sort flat database [In reply to] Can't Post

I am using this code:

# Column num (starting from 0), type of sort
# n = numeric, a = alphabetic ...
# ... Sort order "a" = ascending, "d"= descending,
# ... reference to the array that needs to be sorted & ...
# ... the delimiter between the different column.
$r_data = special_sort(3,"n","a",$r_data);

sub special_sort {
my ($col,$type,$sort_order,$r_data) = @_;
if ($type eq "n") {
@array = map { (split ('<->', $_))[1] }
sort {$a <=> $b}
map { join ('<->', lc ((split('\|', $_))[$col]) , $_) }
@{$r_data};
}
else {
@array = map { (split ('<->', $_))[1] }
sort {$a cmp $b}
map { join ('<->', lc ((split('\|', $_))[$col]) , $_) }
@{$r_data};
}

if ($sort_order eq "d") {
@array = reverse @array;
}

return (\@array)
}

When I am right I now have the option to say I want to search numeric: set $type to "n" using the {$a <=> $b} sort code
and set to "a" {$a cmp $b} to search on alphabeth??
Is that wright???

The reverse part will reverse the sorting.

Is this code OK and if not what is wrong??

 
 


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

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