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:
Sorting data

 



Akotb
Novice

Sep 6, 2016, 1:01 PM

Post #1 of 17 (6450 views)
Sorting data Can't Post

Hello!
I have a problem with sorting data in my program.
While working on the program, there was a problem with sorting the data in the list. Sort does not start, please tell me how to fix the code. Now the program has a row of data for processing

1000001 77.847
1000004 79.914
1234568 89.656
1000001 212.843
1234568 225.265
1000004 234.535

where the first column is represented by an identifier, in the second column of the time, I need to subtract from the current value of the previous time, and write it in the column of the number of times how many of these records in the input data. And then starts sorting, whose time is less tolerated by the higher on the list and the second sorting value, who records on crossing over, the higher on the list is transferred. The output would like to get around it

1. 1000001 134.996
2. 1234568 135.609
3. 1000004 155.621

Now the program looks like this, please tell me how to bring code to mind.


Code
#!perl 

use strict;
use warnings;

open(my $in, "<:encoding(UTF-16)", "INPUT.TXT") || die("Error open INPUT.TXT\n");

# hash sportmans names
my %sportsmans = (
'1000001' => 'First',
'1000004' => 'Second',
'1234568' => 'Third');
my %sportsmans ;
open(my $Nam, "<:encoding(cp1252)", "NAMES.txt") || die("Error open NAMES.TXT\n");
while (my $line = <$Nam>) {
$line =~ /(\d+).(.+?) \s* $/x;
my $id = $1;
my $name = $2;
$sportsmans{$id} = $name;
}
close ($Nam);


# hash results
my %rezults;

while(my $instr = <$in>) {
if ($instr =~ /\@/) {
my $str = $instr;
chop $str; chop $str;
$str =~ m/@\s+(\S.*\S\t?)$/;
my $temp_str = $1;

for (0..1) {
$str = <$in>;
chop $str; chop $str;
$str =~ m/\] (.*)$/;
$temp_str .= $1;
}

$temp_str =~ m/(\d+)\t(\d+\.\d+)/;
unless (exists($rezults{$1})) {
$rezults{$1} = []; # If earlier the pilot did not meet - create a reference to an empty array if the value of the cache pilots with key $ 1
}
push @{$rezults{$1}}, $2; # add the result to the end of the array
}
}

open(my $out, ">", "OUTPUT1.TXT") || die("Error open OUTPUT1.TXT\n");
# reporting results
foreach my $nomer_fio (sort(keys(%sportsmans))){
showResult(\%sportsmans, \%rezults, $nomer_fio);
}
close($out);
close($in);

# function display the results in a particular pilot
sub showResult {
my ($names, $rez, $name) = @_;
my $previousTime = 0; # previous lap time
my $ring = 1; # lap number
print $out "results of the pilot $names->{$name}\n\n";
print $out " lap\current time\lap time\n";


The program displays the results on a specific pilot. And I need to see summarioy result for all pilots
1. 1000001 134.996
2. 1234568 135.609
3. 1000004 155.621
, this requires sorting run. To do this, I change the output process.

Code
sub showReport { 
my ($names, $rez, $name) = @_;
my $previousTime = 0; # previous lap time
my $ring = 1; # lap number
# print $out "results of the pilot $names->{$name}\n\n";
print $out " place | lap |\lapp time\n";


but the code is not running. Please tell me how to run this sort of data.


(This post was edited by Akotb on Sep 12, 2016, 12:17 PM)


Laurent_R
Veteran / Moderator

Sep 6, 2016, 11:20 PM

Post #2 of 17 (6436 views)
Re: [Akotb] Sorting data [In reply to] Can't Post

I don't understand what you're doing.

If this:


Code
1000001 77.847 
1000004 79.914
1234568 89.656
1000001 212.843
1234568 225.265
1000004 234.535

is the input of your first while loop, then it is probably wrong, because when you see the second occurrence of 10000001, you will clubber the previous entry with same ID in the hash.

Otherwise, I can't see any sort procedure in your code.


Laurent_R
Veteran / Moderator

Sep 7, 2016, 1:23 AM

Post #3 of 17 (6432 views)
Re: [Akotb] Sorting data [In reply to] Can't Post

If you have an array containing something like this:


Code
1234568 135.609 
1000001 134.996
1234567 75.786
1000004 155.621


and need to sort it according to the second value of each line, you may use the so-called "Schwartzian transform" (just Google that expression if you don't know it), with something like this:


Code
use strict; 
use warnings;
use Data::Dumper;
my @array = ("1234568 135.609", "1000001 134.996", "1000004 155.621", "1234567 75.786");

my @sorted = map { "$_->[0] $_->[1]" }
sort {$a->[1] <=> $b->[1] }
map { [split / /, $_] }
@array;

print Dumper \@sorted;

Code
 
which prints out:


Code
$VAR1 = [                    
'1234567 75.786',
'1000001 134.996',
'1234568 135.609',
'1000004 155.621'
];


I hope this helps.


Akotb
Novice

Sep 7, 2016, 1:23 AM

Post #4 of 17 (6432 views)
Re: [Laurent_R] Sorting data [In reply to] Can't Post


In Reply To
I don't understand what you're doing.

If this:


Code
1000001 77.847 
1000004 79.914
1234568 89.656
1000001 212.843
1234568 225.265
1000004 234.535

is the input of your first while loop, then it is probably wrong, because when you see the second occurrence of 10000001, you will clubber the previous entry with same ID in the hash.

Otherwise, I can't see any sort procedure in your code.


At the entrance to the program have complicated file. The program parses it here

Code
# hash results  
my %rezults;

while(my $instr = <$in>) {
if ($instr =~ /\@/) {
my $str = $instr;
chop $str; chop $str;
$str =~ m/@\s+(\S.*\S\t?)$/;
my $temp_str = $1;

for (0..1) {
$str = <$in>;
chop $str; chop $str;
$str =~ m/\] (.*)$/;
$temp_str .= $1;
}

$temp_str =~ m/(\d+)\t(\d+\.\d+)/;
unless (exists($rezults{$1})) {
$rezults{$1} = []; # If earlier the pilot did not meet - create a reference to an empty array if the value of the cache pilots with key $ 1
}
push @{$rezults{$1}}, $2; # add the result to the end of the array
}
}


and then constructed it in the following sequence:


Code
1000001 77.847  
1000004 79.914
1234568 89.656
1000001 212.843
1234568 225.265
1000004 234.535


I need to get the following output:

Code
1. 1000001 134.996  
2. 1234568 135.609
3. 1000004 155.621


ie displays a list in which the above list is the one with the least time of the current lap and the most number of laps.

Now the program displays information about each pilot:



Code
Results pilot First  

1 | 153.847
2 | 3.488

Results pilot Second

1 | 157.398
2 | 140.961
3 | 121.305
4 | 115.875
5 | 447.308


in this case it runs in the sorted data lines

Code
foreach my $nomer_fio (sort(keys(%sportsmans))){  
showResult(\%sportsmans, \%rezults, $nomer_fio);
}


But I need to get the following output:

Code
1. 1000001 134.996  
2. 1234568 135.609
3. 1000004 155.621

or maybe

Code
1. First 134.996  
2. Third 135.609
3. Second 155.621


I do not know how I do it. I just learn the language, and it's not easy. Please help, how to change the program so that it exasperated me the right value? Thank you.


Laurent_R
Veteran / Moderator

Sep 7, 2016, 1:26 AM

Post #5 of 17 (6430 views)
Re: [Akotb] Sorting data [In reply to] Can't Post

Please see my previous post, which you may have missed because it was posted exactly at the same time as yours, and see if it helps you doing what you want.


Akotb
Novice

Sep 7, 2016, 2:12 AM

Post #6 of 17 (6427 views)
Re: [Laurent_R] Sorting data [In reply to] Can't Post


In Reply To
Please see my previous post, which you may have missed because it was posted exactly at the same time as yours, and see if it helps you doing what you want.


But the program uses a hash array as input. When I put in your program code, it is not executed.

Here's a clipping from my program, which is the formation of a hash array:

Code
unless (exists($rezults{$1})) { 
$rezults{$1} = []; # If earlier the pilot did not meet - create a reference to an empty array if the value of the cache pilots with key $ 1
}
push @{$rezults{$1}}, $2; # add the result to the end of the array


So i use your code and put it to:


Code
# function display the results in a particular pilot   
sub showResult {
my ($names, $rez, $name) = @_;
my $previousTime = 0; # previous lap time
my $ring = 1; # lap number
#print $out "results of the pilot $names->{$name}\n\n"; #remove this line. We do not need the results of a particular pilot, we need a summary of all at once
print $out " lap\name\current time\lap time\n"; #add the name of the pilot here


I have tried to implement this part of the code under my sort, but I see that there are many errors. File is first displayed, and then made his sorting. I find it difficult to immediately understand how to fix.


(This post was edited by Akotb on Sep 12, 2016, 12:19 PM)


BillKSmith
Veteran

Sep 7, 2016, 8:33 AM

Post #7 of 17 (6413 views)
Re: [Akotb] Sorting data [In reply to] Can't Post

There are several problems with your sort.

The sort function only sorts arrays, not files.

The Schwartzian Transformation is a technique to make complicated sorts run faster. Your have only a small amount of data and your sort criteria is not complicated. I doubt that the transformation would help. It would help to sort your results before converting them to strings.

Names are strings, not numbers. You should compare them with the string compare (cmp) rather than the numerical compare (<=>).


Code
sub showResult { 
my ( $names, $rez, $name ) = @_;
my $previousTime = 0; # previous lap time
my $ring = 1; # lap number

my @unsorted;
foreach my $currTime ( @{ $rez->{$name} } ) {

#my @list = @{$rez->{$name}};
my $delta = sprintf "%.3f", $currTime - $previousTime;

push @unsorted, [$ring, $name, $currTime, $delta];
$ring++;
$previousTime = $currTime;
}
print $out
" lap\name\current time\lap time\n";
my @sorted = sort {$a->[1] <=> $b[1]} @unsorted;
foreach (@sorted) {
my( $ring, $name, $currTime, $delta ) = @$_;
print $out "$ring\{$name}\t$currTime\t$delta\n\n"
}
print $out "\n\n\n";
}

Good Luck,
Bill


Akotb
Novice

Sep 7, 2016, 9:30 AM

Post #8 of 17 (6411 views)
Re: [BillKSmith] Sorting data [In reply to] Can't Post


In Reply To
There are several problems with your sort.

The sort function only sorts arrays, not files.


Thank you for your code, it works, but it works wrong.

the program displays the following information:

Code
  
lap name current time lap time
1{1000005} 153.847 153.847

2{1000005} 157.335 3.488




lap name current time lap time
1{1000006} 157.398 157.398

2{1000006} 298.359 140.961

3{1000006} 419.664 121.305

4{1000006} 535.539 115.875

5{1000006} 982.847 447.308


My code displays the results for each pilot:

The results of the pilot 1
circle time, the current time
1 | 153,847
2 | 3.488

The results of the pilot 2
circle time, the current time
1 | 157,398
2 | 140,961
3 | 121,305
4 | 115,875
5 | 447,308

And I would like to see the results of all pilots were recorded in a single list, and displays only the most recent result. And the program will sort the list by the winners -> the maximum number of laps + minimum amount of time for the current lap.
This means that the results of the available data, the program will display the following information:

Place | Name | Current time | Laps
1 | Pilot 2 | 447,308 | 5
2 | Pilot 1 | 3,488 | 2

The program analyzes the data file, the file is constantly updated. When new information is in the file. a list of pilots is rebuilt accordingly.

I hope I'm clearly explained,
I am confused in these moments.


(This post was edited by Akotb on Sep 7, 2016, 9:32 AM)


BillKSmith
Veteran

Sep 7, 2016, 12:16 PM

Post #9 of 17 (6401 views)
Re: [Akotb] Sorting data [In reply to] Can't Post

We do not seem to be getting anywhere. Lets start with the basics. Correct me wherever I am wrong. You need a program to analyze race data in real time. The data consists of two files. The first associates a contestantís name with an ID number. The second file contains an entry for each time any contestant reaches a checkpoint (probably the start/finish line). Each entry consists of his ID number and his split time (time elapsed since the start of the race). The second file is updated in real-time in much the same way that an err log file is updated.

During the race, you want to display all contestants in the order that they are currently running. For each contestant, you should display their name, current position, and the time it took them to complete the most recent lap.

After the race is over, you need a different display. For each contestant, you need a detailed race history. I an not clear about the contents.

I recommend that we look at this as two programs. (It may be possible to combine them later, but for now, get them working separately.)

Start with the second (end of race) program. It should be the easier because it does not have real-time issues. Please attach a COMPLETE set (both files) of data. It does not have to be real, but it must be realistic. Post the exact output you expect that data to produce. (We will be able to use the same data when we start to work on the other program)

The data you have posted does not appear to be realistic. Most lap times are in the range of 120 to 150. One is about 3 and another over 400. If real, please explain!
Good Luck,
Bill


Akotb
Novice

Sep 7, 2016, 10:45 PM

Post #10 of 17 (6392 views)
Re: [BillKSmith] Sorting data [In reply to] Can't Post

Well, I'll explain as detailed as possible.
As you rightly pointed out - I need a program to analyze race data in real time.
You are right also the destination file. The file from which I read out the data is updated as a log file. At that momeent while the program that creates a log file and fills it, my program reading data from it without any problem, and builds a list of OUTPUT.txt.

During the race, i want to display all contestants in the order that they are currently running (order by winner). For each contestant, i should display their name, current position, and the time it took them to complete the most recent lap, and also the number of current lap.
For example Output.txt:

1 | Jon Snow | 100.125 | 5
2 | Samwell Tarly | 90.101 | 4
3 | Peter Dinklage | 97.154 | 4

As can be seen from the example - the greater the number of laps, the faster pilot. And the less time lap - the faster pilot. Thus working double SORT: by the number of laps and the last lap time.

After the race is over, i dont need a different display.
All output data of the program I need online. To make this information available on the website. To each spectator could see information from phone browser. Thus the displayed information I want to send to the Internet in two ways.

The first - a summary of all the pilots (sort by winner):
1 | Jon Snow | 100.125 | 5
2 | Samwell Tarly | 90.101 | 4
3 | Peter Dinklage | 97.154 | 4

Second - detailed information about the passage of a certain pilot of all his lap:

Results of Jon Snow
1 | 85.135
2 | 88.110
3 | 90.115
4 | 85.462
5 | 100.125

Results of Samwell Tarly
1 | 90.135
2 | 93.110
3 | 95.115
4 | 90.101

Results of Peter Dinklage
1 | 95.135
2 | 98.110
3 | 99.115
4 | 97.154

Now, the program performs only the second part of the output information - detailed information on the passage of a certain pilot of all his laps. A first part of the output information is not working. Please help how to implement it?

About the data i have posted. It is not real competitions, its just testing file. So there are no any real lap times, just test values of time, rough-and-ready, to check the efficiency of the program.

I attached to the post two files - the first file is a file with the input data (INPUT.txt and INPUT1.txt). The second file - a file matching the ID number and name of the pilot (Names.txt).


(This post was edited by Akotb on Sep 12, 2016, 12:20 PM)
Attachments: Names.txt (65 B)


BillKSmith
Veteran

Sep 8, 2016, 3:56 PM

Post #11 of 17 (6366 views)
Re: [Akotb] Sorting data [In reply to] Can't Post

I fail to see how the input that you attached has anything to do with the results you posted. The file INPUT.TXT has only two pilots. INPUT1.TXT has four pilots (only one of which is listed in NAMES.TXT.) None of them completed more than two laps. Your results show three pilots completing several laps each. INPUT.TXT contains the anomaly which I pointed out in my previous post. Tarly completed his second (and final) lab in 3.488 which must be some kind of a record!

I think I am making progress in understanding your problem. I need at least one test case which consists of an input file and the expected results of that file.
Good Luck,
Bill


Akotb
Novice

Sep 8, 2016, 10:42 PM

Post #12 of 17 (6360 views)
Re: [BillKSmith] Sorting data [In reply to] Can't Post


In Reply To
I need at least one test case which consists of an input file and the expected results of that file.

Ok. There are edited versions of input files: Names and Input.
And also file with the information that I would like to get to the output: Myoutput_last.

The input.txt file has time parameter, it is due to built-in clock. Time constantly goes immediately after the equipment turns on and displays the number of seconds (SSS.sss). We compute the pilot's time subtracting from the current value of the previous value of time.

If we reduce the input file. Thus all the drivers crossed the finish line three times. We have already received other statistics presented in Myoutput_interim.txt file.

Thus, the program constantly scans the input file and displays the output file online. The output file is constantly changing depending on changes in the input file.


(This post was edited by Akotb on Sep 12, 2016, 12:15 PM)
Attachments: Names.txt (0.13 KB)
  Myoutput_last.txt (1.95 KB)
  Myoutput_interim.txt (1.11 KB)


BillKSmith
Veteran

Sep 10, 2016, 11:13 AM

Post #13 of 17 (6325 views)
Re: [Akotb] Sorting data [In reply to] Can't Post

This should be close to what you want. It does produce the expected output, at least for this case.

I had to create an auxiliary hash (%aux) to hold all the special data needed for the sort.


Code
use strict; 
use warnings;
use Data::Dumper;

# Data read from file Names.txt in Post #12
# using code pasted from Post #1
my %sportsmans = (
'1000005' => 'Peter Dinklage',
'1000006' => 'Robert Baratheon',
'9887001' => 'Daario Naharis',
'1000001' => 'Jon Snow',
'1000004' => 'Samwell Tarly',
'1234568' => 'Jaime Lannister',
);

# Data read from INPUT.TXT in Post #12
# using code pasted from Post #1
my %rezults = (
'1000001' => [
'107.554', '253.785', '400.589', '560.000',
'709.382', '861.453', '1010.199', '1153.238',
],
'1234568' => [
'81.703', '217.667', '354.691', '491.921',
'631.285', '773.800', '966.925', '1171.632',
],
'1000004' => [
'47.574', '170.425', '292.339', '334.199',
'413.402', '534.960', '657.000', '777.546',
'897.648', '1018.472', '1137.773',
],
'1000005' => [
'71.695', '201.492', '333.976', '463.968',
'593.835', '724.839', '854.015', '983.148',
'1112.136', '1242.781',
],
'1000006' => [
'93.902', '234.171', '375.964', '518.796',
'662.050', '805.207', '947.667', '1092.976',
'1237.113',
],
'9887001' =>
[ '103.242', '242.695', '383.566', '526.097', '671.394', '816.601', ]
);
analyze( \%rezults, \%sportsmans );

sub analyze {
use List::Util qw(min);
my %results = %{ $_[0] };
my %names = %{ $_[1] };
my %aux;
while (my($id, $times) = each %rezults) {
my $name = $names{$id};
my $lap_count = scalar @$times;
my $last_lap_time = $times->[-1] - $times->[-2];
my $best_lap_time = min(@$times); #Ref: post $15
my $last_lap_finish_time = $results{$id}[-1];
$aux{$id} = [
$name,
$lap_count,
$last_lap_time,
$best_lap_time,
$last_lap_finish_time,
];
}
my @order = map {$_->[0]}
sort {$b->[1]<=>$a->[1] or $a->[2]<=>$b->[2] or $a->[3] cmp $b->[3]}
map {[$_, $aux{$_}[1], $aux{$_}[4], $aux{$_}[0]]}
keys %aux
;
my $place = 0;
print "\n\nPlace | Name "
."|Lap time | laps | Best lap time\n\n";
foreach my $pilot (@order) {
printf " %2d | %-20s | %7.3f | %2d | %7.3f\n",
++$place, @{$aux{$pilot}}[0,2,1,3] ;
}
}


OUTPUT:
Place | Name |Lap time | laps | Best lap time

1 | Samwell Tarly | 119.301 | 11 | 47.574
2 | Peter Dinklage | 130.645 | 10 | 71.695
3 | Robert Baratheon | 144.137 | 9 | 93.902
4 | Jon Snow | 143.039 | 8 | 107.554
5 | Jaime Lannister | 204.707 | 8 | 81.703
6 | Daario Naharis | 145.207 | 6 | 103.242

Good Luck,
Bill

(This post was edited by BillKSmith on Sep 10, 2016, 7:55 PM)


Akotb
Novice

Sep 10, 2016, 12:01 PM

Post #14 of 17 (6324 views)
Re: [BillKSmith] Sorting data [In reply to] Can't Post

Thank you! It works perfectly! You are genius!!!


(This post was edited by Akotb on Sep 10, 2016, 12:02 PM)


BillKSmith
Veteran

Sep 10, 2016, 7:47 PM

Post #15 of 17 (6314 views)
Re: [Akotb] Sorting data [In reply to] Can't Post

Oops! My calculation of $best_lap_time is incorrect. I should be:

Code
        my $best_lap_time = $times->[0]; 
foreach (1..$#{$times}) {
$best_lap_time
= min($best_lap_time, $times->[$_] - $times->[$_-1]);
}

Good Luck,
Bill


Akotb
Novice

Sep 10, 2016, 11:11 PM

Post #16 of 17 (6310 views)
Re: [BillKSmith] Sorting data [In reply to] Can't Post

I found a glitch in the program. It was shown before, but I probably did not betray this value. The problem is in the first round. First circle as such does not exist, it is a zero circle from which the countdown begins. System timer starts from the time when the equipment is turned on.
When counting the first round, I consider the following:

my $previousTime = 0;
foreach my $currTime (@{$rez->{$name}}) {
my $delta = sprintf "%.3f", $currTime - $previousTime;
$previousTime = $currTime;}

The time value in the first round is not displayed correctly.
As a result, best lap timel - it's the first lap time. But this is not the case.
First lap display is not required. The total number of laps on one lap less.
But how to implement it?

and I have all the time values are displayed in seconds. Is it possible to implement the transfer of seconds in a minutes?


I solved the problem with the first round in my code. Here's how I did it
my $ ring = 0; Change the value of the first round to 0
and little changed O line
if ($ delta = $ currTime!) {print $ out "$ ring | \ t $ delta \ n \ n"; }

Now I would like to change the reading of the first round in your code. How can I do it?
And how do you recommend to change the time display from seconds to minutes

From
1 | Jaime Lannister | 130.430 | 9 | 89.656
To
1 | Jaime Lannister | 2:10.430 | 8 | 2:10.430


(This post was edited by Akotb on Sep 11, 2016, 10:27 AM)


BillKSmith
Veteran

Sep 11, 2016, 8:48 PM

Post #17 of 17 (6285 views)
Re: [Akotb] Sorting data [In reply to] Can't Post

I am sorry, this forum is not a free code writing service. Laurent and I were only attempting to solve your sorting problem. I wrote a more complete solution because it was fun. Your latest question is not a "glitch". It is a fundamental design problem that nothing to do with perl.

In order for a program such as yours to be "correct", it must conform to the racing rules. I suspect that the large number of input messages which you ignore all have something to do with subtle rules which could affect your results. In other words, the racing rules and the specification of your input file are important parts of your requirements. As an example, does your existing program handle false starts and other disqualifications correctly? I bet you do not even know! I sure do not, and I have little interest in learning enough of your system to find out.
Good Luck,
Bill

 
 


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

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