Hi,

I've got a problem to average lines which have same name.
For exemple:

Strawberries 10 15 14 20
Pineapples 14 17 2 18
Apples 4 12 24 18
Strawberries 20 12 13 24

I would like to average values by fruit name. So, for pineapples, that easy, it's the average of the line. For strawberries, the average must be on the first and the last lines in this exemple.

I think about array, hash and referencing but that doesn't work. Something like create a hash with fruit name in key and a link towards an array containing values, in value of the hash.

Thanks for help!

Here is my code:

open (fruit_file, $file);

#-----------------------------
#Array and hash creation
#-----------------------------

while (my $line = <fruit_file>){

    if ($line =~/(\d+)\s/g){        #to recover values
        $val=$1;    
    }

    if ($line =~/([a-zA-Z])/g){     #to recover fruit names
        $fruit=$1;
        if( !$rh_fruit -> {$fruit}){        #if the rh_fruit hash is empty 
            $rh_fruit -> {$fruit}={[]};     #create an array
        }
        push @{$rh_fruit -> {$fruit} , $val};   #fill the array with values
    }
}
close fruit_file;

#----------------------------
#Values acces
#----------------------------

open (RESULT, ">".$result);

foreach $val(@{$rh_fruit -> {$fruit}}){

    #if ($val ne "NA"){
        $sum += $val;
        $nbval++;   
    #}

$moy = $sum/$nbval;

print RESULT $fruit."\t".$moy."\n";
}
close RESULT;

Recommended Answers

All 7 Replies

Hi Perlie,
There are so many things wrong with your code. But I guess that is because this was just a rough script to get the job done.
Please, check the the code below, it does want you want. Note The result of this code is saved in a file called result.txt.

#!/usr/bin/perl
use warnings;
use strict;

die "Usage: perl_script.pl fruit_file.txt. Please specify you fruit file : $!"
unless defined $ARGV[0];
my $file //= $ARGV[0];    # get the file from CLI i.e script.pl fruit_file.txt
my $aref = [];
open my $fh, '<', $file or die "can't open file: $!";
while (<$fh>) {
chomp;
 if (m/(\w+)\s+?(.+?)$/) {
    push @$aref, { $1 => $2 };
 }
}
close $fh;

open $fh, '>', 'result.txt'
  or die "can't open this file:$!";    # result generate into result file
foreach my $fruit (@$aref) {
my $sum = 0;
  foreach ( keys %{$fruit} ) {
    my $tot = split / /, $fruit->{$_};
    $sum += $_ for split / /, $fruit->{$_};
    print $fh $_, "\t", ( $sum / $tot ), $/;
  }
}
close $fh or die "can't close file:$!";

OUTPUT In result.txt file
Strawberries 14.75
Pineapples 12.75
Apples 14.5
Strawberries 17.25

Thanks 2teez but that's not what I would like to have. Not average by line but by name of the first column. If Strawberries is write several times, the average must be on all of the Strawberries values.

INPUT:

Strawberries 10 15 14 20
Pineapples 14 17 2 18
Apples 4 12 24 18
Strawberries 20 12 13 24

OUTPUT:

Strawberries 16
Pineapples 12.75
Apples 14.5

Where 16 for Strawberries is (10+15+14+20+20+12+13+24)/8
Where 12.75 for Pineapples is (14+17+2+18)/4
And where 14.5 for Apples is (4+12+24+18)/4

Is there a solution?

To create and fill array and hash (no calculate average at this moment) I do this today:

#!/usr/bin/perl
use strict;
use warnings;

my $file=$ARGV[0];

my %h_fruit;
my $rh_fruit=\%h_fruit;  

my $fruit="";
my $val="";

open (fruit_file, $file);

while (my $line = <fruit_file>){
    if ($line =~/(\d+)/g){
        $val=$1;
        #print $val;
    }
    if ($line =~/([a-zA-Z]+)/){
        $fruit = $1;
        if(!$rh_fruit->{$fruit}){
            $rh_fruit->{$fruit}=[];
        }
        push (@{$rh_fruit -> {$fruit}},$val);   
    }
}
close fruit_file;


foreach my $fruit (keys %h_fruit){
    print $fruit." = ".$rh_fruit->{$fruit}."\n";
}

But I 've got this in output:

Pineapples = ARRAY(0x235ea8)
Apples = ARRAY(0x236bbc)
Strawberries = ARRAY(0x235dac)

Is that a problem with the derefencing in the print?

An other problem is that this script don't keep all values of the line but only the first (10 14 4 20).

How to solve them?

Your idea of a hash with keys of fruit names and values as array references should be sufficient. In your last script you convert the hash to a hash reference. You don't need a hash reference in this case and it makes the code look more complex when you want to dereference your array. The following should work:

#!/usr/bin/perl
use strict;
use warnings;
use List::Util qw(sum);#You probably have this module.

my $file=$ARGV[0];
my %h_fruit;#Keys will be fruits. Values will be references to arrays of values.

#Always test that file open worked, and die if it failed
#Best practice: open lexical filehandle such as $fh instead of bareword such as fruit_file
open (my $fh, '<', $file) or die "Failed to open $file: $!";

while (my $line = <$fh>){
    chomp $line;
    #You can use your regex but split on spaces looks simpler to me.
    my @fields = split(/\s+/, $line);#split on spaces
    my $fruitname = shift(@fields); #Remove the first field and assign it to $fruitname
    $h_fruit{$fruitname} = [] unless exists($h_fruit{$fruitname});
    push $h_fruit{$fruitname}, @fields;
}

foreach my $fruit (keys %h_fruit){
    my @values = @{$h_fruit{$fruit}};#Dereference the array reference
    my $nbval = @values;#Number of fields in the array
    my $sum = sum(@values);
    my $moy = $sum / $nbval;
    print $fruit, ' Average: ', $moy, "\n";
}

Nice one d5e5! That solves the problems for perlie.

Thanks a lot d5e5! Your script makes me learn some new things!

P.s: An error occurs at line 19th solved by an arobase: push @{$h_fruit{$fruitname}}, @fields;

P.s: An error occurs at line 19th solved by an arobase: push @{$h_fruit{$fruitname}}, @fields;

For some reason perl didn't give me an error or warning about line 19 and it seemed to work OK, but you are correct: a good way to dereference the array reference $h_fruit{$fruitname}, as I needed to do in order to push values into it, is to enclose it in curly brackets and prefix it with an @, as you did.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.