I have grades.tsv file with three columns that show students' names, subjects and grades:
Liam Mathematics 5
Liam History 6
Liam Geography 8
Liam English 8
Aria Mathematics 8
Aria History 7
Aria Geography 6
Isabella Mathematics 9
Isabella History 4
Isabella Geography 7
Isabella English 5
Isabella Music 8
I wanted to calculate the average grade for each student and add it to a separate column. For this I used two filehandles DATA and OUT opening the same file:
use strict;
use warnings;
# Open file with grades for calculation of average grade for each student
open (DATA,"grades.tsv") or die "Cannot open file\n";
my %grade_sums;
my %num_of_subjects;
# Calculate sum of grades and number of subjects for each student
while( <DATA> ) {
chomp;
my ($name, $subject, $grade) = split /\t/;
$grade_sums{$name} += $grade;
$num_of_subjects{$name} += 1;
}
close DATA;
# Open file with grades again but this time for a purpose of adding a separate column with average grade and printing a result
open (OUT,"grades.tsv") or die "Cannot open file\n";
while ( <OUT> ) {
chomp;
my ($name, $subject, $grade) = split /\t/;
# Calculate average grade
my $average_grade = $grade_sums{$name} / $num_of_subjects{$name};
my $outline = join("\t", $name, $subject, $grade, $average_grade);
# Print a file content with new column
print "$outline\n";
}
close OUT;
The code works but I am not sure if it is a proper way for this task. Is it a good practice or are there better ways that should be preferred?
Reopening the file is fine. One alternative would be to seek to the start of the file.
use Fcntl qw( SEEK_SET );
seek(DATA, 0, SEEK_SET);
Seeking is more efficient because it doesn't have to check for permissions, etc. It also guarantees that you get the same file (but not that noone changed it).
The other alternative would be to load the entire file into memory. That's what I'd usually do.
Note that
open(FH, $qfn) or die "Cannot open file\n";
is better written as
open(my $FH, '<', $qfn)
or die("Can't open file \"$qfn\": $!\n");
open
avoids some problems.DATA
as Perl sometimes creates a handle by that name automatically.FH
) in favour or lexical variables (my $FH
).