2

I have data in text file. It's only name and points. I need to remove duplicate names and count the average of points. I created a struct and read the file into struct array. Now how can I manipulate data in array? How can I take persons, who is in file more than one time, points and calculate average? Perl is new language for me and I don't know syntax well. My code:

use Class::Struct; 
use warnings;
use strict;   

struct Person => {
    name => '$',
    points => '$'};


my $filename = 'data.txt';
open(my $fh, '<:encoding(UTF-8)', $filename)
    or die "Could not open file\n";

my @resultArray;
my @name;
my @name2;
my @grade2;
my @grade;
my @nameArray;
my @gradeArray;

my $person = Person->new();


while (my $row = <$fh>) {
    chomp $row;
    (@name, @grade) = split("\t", $row);
    push(@nameArray, @name);
    #($person->name, $person->points) = split("\t", $row);  
    #push(@nameArray, @name);

}
foreach(@nameArray) {
my @seperated = split(' ', $_);
$person->name($seperated[0]);
$person->points($seperated[1]);
}

print($person->points);
8
  • 3
    (@name, @grade) = split("\t", $row); is wrong, you can't assign to two arrays at the same time - the first one takes all the elements of the RHS. Commented Dec 2, 2018 at 21:34
  • Please show some of that data file Commented Dec 2, 2018 at 23:16
  • @zdim data file looks this but in seperate lines. In one line there is only one name and points: John 5 Smith 8 Mae 4 Walker 9 Commented Dec 3, 2018 at 0:38
  • Then the code with arrays makes no sense (on top of being wrong as @choroba noted) since the split returns exactly two strings, right for ($name, $points) scalars. The data should be specifed in the question and I'd suggest that you edit it and add that. Commented Dec 3, 2018 at 0:52
  • Re: "I need to remove duplicate names and count the average of points." -- do you mean to find average points for all entries for a duplicated name and produce one entry for that name with those points? Commented Dec 3, 2018 at 1:04

1 Answer 1

2

To have an array entry for each person you need to create a new object for each, inside the loop. Once you've read the data you'd have to post-process that to establish duplicates and remove all but one entry.

That is inefficient, with extra calls to constructor and destructor, and deletions from an array.

Thus I'd suggest to read data into a hash instead, in which way you can also process duplicates as you go. Then populate an array with struct objects for further work.

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

use Class::Struct; 

struct Person => { name => '$', points => '$' };

my @people; 

PREPARE_DATA: {    
    my (%people, %cnt);

    while (<DATA>) {
        my ($name, $pts) = split;
        if (exists $people{$name}) {
            $people{$name} += $pts;
            ++$cnt{$name};
        }   
        else { 
            $people{$name} = $pts;
        }   
    }

    foreach my $name (keys %cnt) { 
        $people{$name} /= $cnt{$name}+1;
    }    

    # Now populate an array with objects
    foreach my $name (sort keys %people) {    
        push @people, Person->new(name => $name, points => $people{$name});
    }
};

for my $p (@people) {
    say $p->name, ' --> ', $p->points;
}

__DATA__
Joe 12
John 20
Joe 24
John 40
Joe 36
Matt 15

I put all processing in a block (which I named PREPARE_DATA) to limit the scope of the ancillary variables used; after the block only the desired @people exists.

The data is supplied using the __DATA__ section, so that all data used here is readily seen.

The test for existence, whereby the count of only dupes is kept, is not necessary. One can add points and increase the count for each person, and later divide all by their counts. Checking only dupes is a little more efficient and, more importantly, lends itself more readily to checking other kinds of properties.

Sign up to request clarification or add additional context in comments.

1 Comment

@GR2096 Edited, as I forgot to include data after __DATA__ and the printout of results

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.