finding highest value in hash - perl

I have a hash with 5 keys, each of these keys have 5 values
foreach $a(#mass){
if($a=~some regex){
#value=($1,$2,$3,$4,$5);
$hash{"keysname$c"}="#value";
c++;
}
}
Each scalar is a value of different parameters , I have to determinate the highest value of the first array for the all keys in hash
Edit:
Code must compare first value of key1 with first value of key2, key3...key5 and print the highest one

This will print max value for structure like
my %hash = ( k1 => [6,4,1], k2 => [16,14,11] );
use List::Util qw(max);
# longest array
my $n = max map $#$_, values %hash;
for my $i (0 .. $n) {
my $max = max map $_->[$i], values %hash;
print "max value on position $i is $max\n";
}
and for strings,
my %hash = ( k1 => "6 4 1", k2 => "16 14 11" );
use List::Util qw(max);
# longest array
my $n = max map $#{[ split ]}, values %hash;
for my $i (0 .. $n) {
my $max = max map [split]->[$i], values %hash;
print "max value on position $i is $max\n";
}

If I understand your question correctly (and it's a little unclear) then I think you want something like this:
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use List::Util 'max';
my (#data, #max);
while (<DATA>) {
chomp;
push #data, [split];
}
for my $i (0 .. $#{$data[0]}) {
push #max, max map { $_->[$i] } #data;
}
say "#max";
__DATA__
93 3 26 87 7
66 96 46 77 42
26 3 71 64 91
31 27 14 40 86
82 72 71 34 7

try this
map {push #temp, #{$_}} values %hash;
#desc_sorted= sort {$b <=> $a} #temp;
print $desc_sorted[0],"\n";
map will consolidate all lists to a single list and sort will sort that consolidated array in descending order.

Related

Perl: Generating an Array of hashes from a file

I'm trying to create an array of hashes that contains student names as the keys and multiple grades as the values for each student so that I can compute the average for each student, sort the averages in descending order, and print the "lastname, firstname: grade average" of each student in the sorted order.
The issue i'm having is with the generation of the array of hashes which I'm 90% sure the problem lies in the split on line 10 but I can't seem to find the solution myself.
students.txt:
chipper jones 29 80 70
hank aaron 99 85 81 75
beth allen 64 84 71 5x9 38 68 53
andruw jones 100 100 100 100 100
ty cobb 75 75 100
code:
#!/usr/bin/perl
#Program 5
my #Aoh;
open (FILEIN, "$ARGV[0]");
while(<FILEIN>) {
chomp;
push #Aoh, { split / / };
for $i (0 .. $#Aoh) {
print "{ ";
for $role (keys %{ $Aoh[$i] }) {
print " $role $Aoh[$i]{$role} ";
}
print "}\n";
}
}
output I'm getting:
0 is { chipper=jones 70= 29=80 }
1 is { 81=75 hank=aaron 99=85 }
2 is { 38=68 53= beth=allen 64=84 71=5x9 }
3 is { 100= andruw=jones }
4 is { ty=cobb 75=75 100= }
push #Aoh, { split / / };
Creates the following hash from the first line:
{ chipper => 'jones',
29 => 80,
70 => undef,
}
That's not what you wanted, right?
I'd use a hash of numbers instead of the array of hashes. You can use "lastname, firstname" directly as the hash key and you can store the averages directly as the values:
#!/usr/bin/perl
use warnings;
use strict;
use List::Util qw{ sum };
my %average;
while (<>) {
my ($name, $surname, #grades) = split;
$average{"$surname, $name"} = sum(#grades) / #grades;
}
for my $student (sort { $average{$a} <=> $average{$b} } keys %average) {
print $student, ' ', $average{$student}, "\n";
}
Note that I'm getting a warning:
Argument "5x9" isn't numeric in subroutine entry at ./1.pl line 11, <> line 3.
How should one treat the 5x9 grade?

How to sort an array by substring, perl

So, for example i have array like that:
my #arr = (
"blabla\t23\t55",
"jkdcbx\t55\t89",
"jdxjcl\t88\t69",
......)
And i need to sort this array by second column after \t, without outer splits. Is it possible to do?
May be a more elegant way but this will work :
my #arr = ( "blabla\t23\t55", "jkdcbx\t55\t89", "jdxjcl\t88\t69");
for (sort {(split(/\t/,$a))[2] <=> (split(/\t/,$b))[2]} #arr) {
print "$_\n";
}
Update
I've just realised that your question may mean that you want to sort by the third column instead of the second
That would be done by using
my ($aa, $bb) = map { (split /\t/)[2] } $a, $b;
instead
output
blabla 23 55
jdxjcl 88 69
jkdcbx 55 89
I always prefer to use map to convert the values from the original data into the function that they should be sorted by
This program demonstrates
I assume you want the values sorted numerically? Unfortunately your example data is already sorted as you describe
use strict;
use warnings 'all';
use feature 'say';
my #arr = (
"blabla\t23\t55",
"jkdcbx\t55\t89",
"jdxjcl\t88\t69",
);
my #sorted = sort {
my ($aa, $bb) = map { (split /\t/)[1] } $a, $b;
$aa <=> $bb;
} #arr;
say for #sorted;
output
blabla 23 55
jkdcbx 55 89
jdxjcl 88 69
Try this
use warnings;
use strict;
no warnings "numeric";
my #arr = (
"blabla\t23\t55",
"jkdcbx\t85\t89",
"jdxjcl\t83\t69",
);
my #result = sort {$a=~s/^[^\t]*\t//r <=> $b=~s/^[^\t]*\t//r } #arr;
$, = "\n";
print #result,"\n";
I have used following technique with sort for to do it
Negation character class
Non-destructive modifier(-r) - perform non-destructive substitution and return the new value
And tured of the warning for numeric

Backslash before a subroutine call

As I was understanding the difference between [] and \ in references,I used both on subroutine the former was fine but when I tried later I thought it should give error but the below program in perl
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my #b;
for my $i ( 0 .. 10 ) {
$b[$i] = \somefunc($i);
}
print Dumper( \#b );
sub somefunc {
my $n = shift;
my ( #a, $k );
for my $j ( 11 .. 13 ) {
$k = $n * $j;
push( #a, $k );
}
print "a: #a \n";
return #a;
}
gives output as :
a: 0 0 0
a: 11 12 13
a: 22 24 26
a: 33 36 39
a: 44 48 52
a: 55 60 65
a: 66 72 78
a: 77 84 91
a: 88 96 104
a: 99 108 117
a: 110 120 130
$VAR1 = [
\0,
\13,
\26,
\39,
\52,
\65,
\78,
\91,
\104,
\117,
\130
];
I was unable to understand the output.Need explanation.
What is happening here is:
You are returning an array from somefunc.
But you are assigning it to a scalar. What this is effectively doing therefore, is simply putting the last value in the array, into the scalar value.
my $value = ( 110, 120, 130 );
print $value;
When you do this - $value is set to the last value in the array. This is what's actually happening in your code. See for example perldata:
List values are denoted by separating individual values by commas (and enclosing the list in parentheses where precedence requires it):
(LIST)
In a context not requiring a list value, the value of what appears to be a list literal is simply the value of the final element, as with the C comma operator. For example,
#foo = ('cc', '-E', $bar);
assigns the entire list value to array #foo, but
foo = ('cc', '-E', $bar);
assigns the value of variable $bar to the scalar variable $foo. Note that the value of an actual array in scalar context is the length of the array; the following assigns the value 3 to $foo:
#foo = ('cc', '-E', $bar);
$foo = #foo; # $foo gets 3
It's this latter case that's often the gotcha, because it's a list in a scalar context.
And in your example - the backslash prefix denotes 'reference to' - which is largely meaningless because it's a reference to a number.
But for a scalar, it might be more meaningful:
my $newvalue = "fish";
my $value = ( 110, 120, 130, \$newvalue );
print Dumper $value;
$newvalue = 'barg';
print Dumper $value;
Gives:
$VAR1 = \'fish';
$VAR1 = \'barg';
That's why you're getting the results. Prefix with the slash indicates that you're getting a reference to the result, not a reference to the sub. Reference to 130 isn't actually all that meaningful.
Normally, when doing the assignment above - you'd get a warning about Useless use of a constant (110) in void context but this doesn't apply when you've got a subroutine return.
If you wanted to insert a sub reference, you'd need to add &, but if you just want to insert the returned array by reference - you either need to:
$b[$i] = [somefunc($i)]
Or:
return \#a;

How to show top values from hash in perl?

Let's say I have the following info stored in hash:
kiwi 15
oranges 25
cherries 30
apples 2
pears 1
I want to write code that would display in the descending (by amount) top 3 entries.
So the output should be
cherries 30
oranges 25
kiwi 15
I can't seem to find the clear answer on that.
This will do as you ask. It uses sort with a comparison block that compares each key's corresponding hash value in reverse order. Then each of the first three sorted keys is printed along with its value from the hash.
Note that, if there are multiple hash elements with the same highest value, then this code will print an arbitrary three elements out of those that share the same value
use strict;
use warnings;
my %data = qw/
kiwi 15
oranges 25
cherries 30
apples 2
pears 1
/;
my #sorted_keys = sort { $data{$b} <=> $data{$a} } keys %data;
for my $key ( #sorted_keys[0..2] ) {
print "$key $data{$key}\n";
}
output
cherries 30
oranges 25
kiwi 15
Update
For a more general solution, the (non-core) List::UtilsBy module offers a number of utility functions that offer sorts, maxima and minima as a function of the object list. It lets me write the above as
use List::UtilsBy qw/ nsort_by /;
my #sorted_keys = nsort_by { $data{$_} } keys %data;
for my $key ( (reverse #sorted_keys)[0..2] ) {
print "$key $data{$key}\n";
}
or, if you prefer the reverse in a different place
use List::UtilsBy qw/ rev_nsort_by /;
my #sorted_keys = rev_nsort_by { $data{$_} } keys %data;
for my $key ( #sorted_keys[0..2] ) {
print "$key $data{$key}\n";
}
Observe that the difference between the module's sort_by and nsort_by is equivalent to the difference between the cmp and <=> comparison operators, respectively.
Both of these alternatives generate identical output to the original above

Perl subroutine assistance

Using Data::Dumper and List::Util I'm able to sum the total of each row within my array with a subroutine. This part is correct.
With an easier approach I attempted to print the grand total of all numbers with a separate subroutine called get_grandtotal. This returns incorrect numbers.
My question is how do I print the correct grand total?
And what modifications would I use to print the column total (instead of the row total) using a similar structure in get_row(#values).
#!/usr/bin/perl
use 5.10.1;
use warnings;
use strict;
use List::Util qw(sum);
use Data::Dumper;
my #values = (
[ 6, 5, 13 ],
[ 35, 9, 6 ],
[ 65, 255, 54 ]
);
get_row(#values);
sub get_row {
my #total;
foreach my $row (#_) {
say join ' ', #$row;
push #total, sum #$row;
}
say Data::Dumper->Dump( [ \#total ], [ qw(*Row_Total) ] );
}
my $sum = 0;
sub get_grandtotal() {
foreach (#values) {
$sum += $_;
}
print "Grand Total = $sum\n";
}
get_grandtotal();
Output
6 5 13
35 9 6
65 255 54
#Row_Total = (
'24',
'50',
'374'
);
Grand Total = 61899232
You are trying to add together array references in
$sum += $_;
change this to
$sum += sum #$_;
and your code will work.
This subroutine uses map to extract the columns from the array, and prints the totals
sub get_column {
my #total;
foreach my $i (0 .. $#{$values[0]}) {
my #column = map $_->[$i], #values;
say join ' ', #column;
push #total, sum #column;
}
say Data::Dumper->Dump( [ \#total], [ qw(*Column_Total) ] );
}
output
6 35 65
5 9 255
13 6 54
#Column_Total = (
'106',
'269',
'73'
);