Home > Back-end >  Add the scores using perl with its respective person
Add the scores using perl with its respective person

Time:12-18

I have a file which contains data with many persons & their scores. i am trying to take avg of scores of each person.

Person 1
Scores (\
"0.06, 0.01, 0.07, 0.07, 0.75", \
"0.05, 0.08, 0.01, 0.09, 0.08", \
"0.10, 0.10, 0.11, 0.12, 0.10", \
"0.18, 0.19, 0.20, 0.20, 0.19", \
"0.31, 0.32, 0.32, 0.33, 0.32");
}
Person 2
Scores (\
"0.06, 0.01, 0.07, 0.07, 0.75", \
"0.05, 0.08, 0.01, 0.09, 0.08", \
"0.10, 0.10, 0.11, 0.12, 0.10", \
"0.18, 0.19, 0.20, 0.20, 0.19", \
"0.31, 0.32, 0.32, 0.33, 0.32");
}

Expected Output

Person 1 - (avg value)
Person 2 - (avg value)

What i tried:

open($in, “<file.txt>”)
    or die;

while(<$in>) {
    if (/Person/) {
        if (/Scores/../}/) {
            $_ =~ s/,//g;
            $_ =~ s/\\//g;  # removing all unwanted characters to take avg of numbers 
            $_ =~ s/"//g;
            $_ =~ s/values//g;
            $_ =~ s/\(//g;
            $_ =~ s/\)//g;
            $_ =~ s/;//g;
            $_ =~ s/}/ /g;
            @a1 = split(" ",$_);
        }
    }
}

After this point i am not able to store the values in array for further computation.

CodePudding user response:

The fundamental problem with your code is you are walking a line at a time through your input data, but you code is making the assumption that all the parts you need to parse the code are present in that one line.

For example these two statements are checking first for the literal Person on the current line, and then checking for the literal string Scores on the same line. That will never match -- the two literal strings are on different lines

    if (/Person/) {
        if (/Scores/../}/) {

There are lots of approaches to this problem, here is one of them.

use strict;
use warnings ;

use List::Util qw(sum);


# read the complete file into $data
my $data ;
{
    local $/;
    $data = <DATA>;
}

# repeatedly match each Person/Scores section
while ($data =~ /Person\s (\S )\s Scores\s \((. ?)\)/smg)
{
    my $person = $1;
    my $scores = $2;

    # now split $scores into the individual values - store in @scores
    my @scores;
    while ($scores =~ /(\d \.\d )/smg)
    {
        push @scores, $1
    }

    # @scores now holds the individual values. 
    # Can work out the average from them
    my $average = sum(@scores) / scalar @scores;

    print "Person $person - $average\n";
}

__DATA__
Person 1
Scores (
"0.06, 0.01, 0.07, 0.07, 0.75", 
"0.05, 0.08, 0.01, 0.09, 0.08", 
"0.10, 0.10, 0.11, 0.12, 0.10", 
"0.18, 0.19, 0.20, 0.20, 0.19", 
"0.31, 0.32, 0.32, 0.33, 0.32");
}
Person 2
Scores (
"0.06, 0.01, 0.07, 0.07, 0.75", 
"0.05, 0.08, 0.01, 0.09, 0.08", 
"0.10, 0.10, 0.11, 0.12, 0.10", 
"0.18, 0.19, 0.20, 0.20, 0.19", 
"0.31, 0.32, 0.32, 0.33, 0.32");
}

output is

Person 1 - 0.1744
Person 2 - 0.1744

CodePudding user response:

Based on provided data structure we can utilize } as record separator, it simplifies the task.

Then it is just a matter of extracting pieces of information from each block and make simple computation.

use strict;
use warnings;
use feature 'say';

$/ = '}';

while( my $record = <DATA> ) {
    next unless $record =~ /Person (\d)/;
    my $data;
    $data->{person} = $1; 
    $data->{scores}->@* = $record =~ /(\d\.\d{2})/gsm;
    $data->{sum}         = $_ for @{$data->{scores}};
    $data->{count}      = scalar $data->{scores}->@*;
    $data->{average}    = $data->{sum}/$data->{count};
    say "Person: $data->{person} - ($data->{average})";
}

exit 0;

__DATA__
Person 1
Scores (\
"0.06, 0.01, 0.07, 0.07, 0.75", \
"0.05, 0.08, 0.01, 0.09, 0.08", \
"0.10, 0.10, 0.11, 0.12, 0.10", \
"0.18, 0.19, 0.20, 0.20, 0.19", \
"0.31, 0.32, 0.32, 0.33, 0.32");
}
Person 2
Scores (\
"0.06, 0.01, 0.07, 0.07, 0.75", \
"0.05, 0.08, 0.01, 0.09, 0.08", \
"0.10, 0.10, 0.11, 0.12, 0.20", \
"0.18, 0.19, 0.20, 0.20, 0.19", \
"0.31, 0.32, 0.32, 0.33, 0.32");
}

Output

Person: 1 - (0.1744)
Person: 2 - (0.1784)
  • Related