Sum all values in each(hash of Array) perl - arrays

I have a hash of array named %hash
$VAR1 = {
'reboot' => [
4442,
3483,
541
],
'prod-dev' => [
0,
485,
3421,
242,
425,
425,
484,
1,
244
]
};
How to add all the values in each key and print them all at once like this
reboot : sum
prod-dev : sum
thanks cheers
#!/usr/binkj/perl -w
use strict;
use warnings;
use List::Util qw( sum );
use Data::Dumper;
my ($substr, $new_line);
my #fields;
my %hash =();
open(my kj$logs, ">STDOUT") or die $!;
my ($total_sum, $total_t, $tsum);
my (#array, $key, $val);
while (<STDIN>) {
my #matches;
chomp;
next if $_ =~ m/still logged in/;
next if $_ =~ m/wtmp/;
next if $_ =~ m/\(-\d.+?\)/;
next if $_ =~ m/^$/g;
$_ =~ /(^.*?)\s.*?(\(.*?\))/g;
($key, $val) = ($1,$2);
$val =~ s/(\(|\))//g;
if($val =~ m/\d\+.*/){
$val =~ s/\+/\:/;
#$val =~ m/(^\d.)(:\d.:)(:\d.\s)/g;
my ($days, $hrs, $mins) = $val =~ /^(\d+):(\d+):(\d+).$/g;
$days = $days * 24 * 60;
$hrs = $hrs * 60;
$total_t = $days + $hrs + $mins;
#print $days . ":" . $hrs . ":" . $mins. "\n";
print "$total_t \n";
}else {
my ($hrs, $mins) = $val =~ /^(\d+):(\d+).$/g;
$hrs = $hrs * 60;
$total_t = $hrs + $mins;
print "$total_t \n";
}
push (#{$hash{$key}}, $total_t);
for my $k (keys(%hash)) {
printf("%-8s : %d\n", $k,sum( #{ $hash->{$k} } ),);
}
print Dumper (\%hash);
close $logs;
here is the whole perl program
im really having a hard time solving this one wew I hope you guys can help me

use List::Util qw( sum );
for my $k (keys(%$VAR1)) {
printf("%-8s : %d\n",
$k,
sum( #{ $VAR1->{$k} } ),
);
}

minor but important difference from above answer in accordance with question and without any external module.
foreach $val (keys(%$VAR1)){
$s=0;
foreach ( #{$VAR1->{$val}}){
$s = $_+$s;
}
print "$val :- $s ";
}

I managed to answer it guys, thanks you very much!
foreach my $key ( keys %hash ) {
my $total_sum = 0;
foreach ( #{$hash{$key}} ) {
$total_sum += $_;
}
print $key . "" . $total_sum . "\n";
}

The function sum from List::Util can be used for the sum:
#!/usr/bin/env perl
use strict;
use warnings;
my %hash = (
'reboot' => [ 4442, 3483, 541 ],
'prod-dev' => [ 0, 485, 3421, 242, 425, 425, 484, 1, 244 ],
);
use List::Util qw( sum );
my #sums;
for my $key ( keys %hash ) {
my $sum = sum #{ $hash{$key} };
push #sums, "$key: $sum\n";
}
print #sums;

Related

Perl: How to access Array of Hashes passed to a Subroutine

Have a perl script reading from an xml, parsing the data into hashes in one sub, outputting an array of hashes and then from main calling a second sub to process the array of hashes.
Data::Dumper shows that everything is being passed properly.
Having a terrible time figuring out why I can't now access the hashes.
use strict;
use warnings;
use Data::Dumper;
my (#sortedData, $value1, $value2);
use subs qw(processData outputData);
#sortedData = processData;
outputData($value1, $value2, \#sortedData);
sub processData{
# Example code
# # Does some processing of xml that results in a hash.
# # That series of hashes is pushed onto an array
my ($item, #results);
# foreach $item ( #{ $rss->{items}){
# my %data = (
# 'first' => $item->{'value'},
# 'second' => $item->{'value'},
# 'third' => $item->{'value'}
# );
# push #results, \%data;
# }
# Essentially the hash is :
#results = (
{'data1'=>810,'data2'=>153,'data3'=>215},
{'data1'=>160,'data2'=>220,'data3'=>604},
{'data1'=>940,'data2'=>103,'data3'=>115},
{'data1'=>100,'data2'=>319,'data3',525},
{'data1'=>500,'data2'=>803,'data3'=>650}
);
return #results;
}
sub outputData{
my ($input1, $input2, #localData) = #_;
print Dumper #localData;
print "\#localData: " . #localData . "\n";
foreach my $i (#localData){
# foreach my $j ($i){
# print $i . "\n" . $j . "\n";
# }
print "\$i: " . $i . "\n";
}
}
The output:
$VAR1 = [
{
'data3' => 215,
'data2' => 153,
'data1' => 810
},
{
'data3' => 604,
'data2' => 220,
'data1' => 160
},
{
'data2' => 103,
'data3' => 115,
'data1' => 940
},
{
'data1' => 100,
'data3' => 525,
'data2' => 319
},
{
'data1' => 500,
'data3' => 650,
'data2' => 803
}
];
#localData: 1
$i: ARRAY(0x80071b30)
I don't understand why the value of #localData is 1 or why the reference for an element of #localData is ARRAY instead of HASH.
I am certainly testing my deeper understanding of PERL arrays for the first time.
I don't understand it as much as I thought.
You pass a reference to the array to the sub, then assign this single scalar to #localData. Fix:
sub outputData{
my ($input1, $input2, $localData) = #_;
print Dumper $localData;
print "\#localData: " . #$localData . "\n";
foreach my $i (#$localData){
...
}
}

Perl: Dump data from a hash into excel

I have a hash with key and Values(array). I want to dump them to a spreadsheet, but having difficulty in arranging them .
%hash
key1 -> foo bar
key2-> john adam gill
key3-> apple banana mango orange
Code:
use strict;
use warnings;
use Excel::Writer::XLSX;
my $pattern = "BEGIN_";
my $format;
my #keys = qw(key1 key2 key3);
foreach my $key(#keys){
open my $fh, "<","filename.txt" or die $!;
while ( <$fh> ) {
if (/$pattern/) {
push(#matching_lines, $_);
}
}
$hash{$key} = [#matching_lines] ;
for (#matching_lines) { $_ = undef } ; #Emptying the array contents,to reuse it for for all the other keys
}
my $workbook = Excel::Writer::XLSX->new( 'c:\TEMP\filename.xlsx' );
if (not defined $workbook)
{
die "Failed to create spreadsheet: $!";
}
my $worksheet = $workbook->add_worksheet();
# Add and define a format
$format = $workbook->add_format();
$format->set_bg_color( 'yellow' );
my $row = 1;
my $col = 0;
foreach my $k (keys %hash)
{
$worksheet->write($row, $col, $k, $format); # title
$worksheet->write_col($row+1, $col, $hash{$k}); #value
$col++;
}
$workbook->close() or die "Error closing file: $!";
Current Output
Desired Output
Edit: Now you've actually updated your program to clarify that the problem is how you're reading your data, the below is moot. But it does illustrate an alternative approach.
OK, the core problem here is what you're trying to do is 'flip' a hash. You're printing row by row, but your hash is organised in columns.
Using comma sep as a quick proxy for printing actual excel:
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
#initialise
my %hash = (
key1 => [qw ( foo bar )],
key2 => [qw ( john adam gill )],
key3 => [qw ( apple banana mango orange )],
);
#print for debug
print Dumper \%hash;
#get header row. Sort it, because hashes are unordered.
#could instead:
#my #keys = qw ( key1 key2 key3 );
my #keys = sort keys %hash;
#print header row
print join ",", #keys, "\n";
#iterate until every element of the hash is gone
while ( map { #{ $hash{$_} } } #keys ) {
#cycle the keys, shifting a value of the top of each array.
#replace any undefined values with ''.
print shift( #{ $hash{$_} } ) // '', "," for #keys;
print "\n";
}
This prints:
key1,key2,key3,
foo,john,apple,
bar,adam,banana,
,gill,mango,
,,orange,
Which if you load it as csv into Excel, should give your desired result. I'm pretty sure you could use a similar 'write row' with the module.
So this actually seems to do what you want:
#!/usr/env/perl
use strict;
use warnings;
use Excel::Writer::XLSX;
#initialise
my %hash = (
key1 => [qw ( foo bar )],
key2 => [qw ( john adam gill )],
key3 => [qw ( apple banana mango orange )],
);
my $workbook = Excel::Writer::XLSX->new('c:\TEMP\filename.xlsx');
if ( not defined $workbook ) {
die "Failed to create spreadsheet: $!";
}
my $worksheet = $workbook->add_worksheet();
# Add and define a format
my $format = $workbook->add_format();
$format->set_bg_color('yellow');
my #keys = sort keys %hash;
my $row = 0;
$worksheet->write_row( $row++, 0, \#keys, $format );
while ( map { #{ $hash{$_} } } #keys ) {
my $col = 0;
$worksheet->write( $row, $col++, shift( #{ $hash{$_} } ) // '' )
for #keys;
$row++;
}
$workbook->close() or die "Error closing file: $!";
You're not correctly emptying your #matching_lines array. This line:
for (#matching_lines) { $_ = undef }
Sets the array values to undef, but does not remove them.
For example, if #matching_lines was ('foo', 'bar'), now it becomes (undef, undef). When you add baz and qux to it later, it becomes (undef, undef, 'baz', 'qux'). These undefs become blank cells when you add them to the worksheet.
To correctly empty the array, use:
#matching_lines = ();

First and last regex match

Hi I have a problem with my program, I have wrote the code below and it returns the expected result. However I only am intrested in the first and last occurance of the matches. How would I go about doing this?
foreach (#array)
{
$element = $_;
foreach(#array2)
{
if($_ =~ s/($element)//ig)
{
print "$_ \n";
}
}
}
Currently the loop goes through every element in the array finds it in the second array and prints the whole line. It returns the expected result, however I want the first match and last match.
foreach my $elm2 (#array2) {
my $state = 'start';
my $first, $last;
foreach my $elm1(#array1) {
if (($state eq 'start') && ($elm1 =~ m/$elm2/i)) {
$first = "$elm1";
$state = 'last';
}
elsif (($state eq 'last') && ($elm1 =~ m/$elm2/i)) {
$last = $elm1;
}
}
print "$elm2,$first,$last\n";
}
Could maybe do this
foreach (#array)
{
$first = "";
$last = "";
$element = $_;
foreach(#array2)
{
if($_ =~ s/($element)//ig)
{
if (!length($first)){
$first = $_;
}
else {
$last = $_;
}
}
}
if (length($first) && length($last)) {
print "\n----------\nfirst = '$first'\nlast = '$last'";
}
}
Totally forgot about grep.
foreach my $elm2 (#array2) {
my #matches = grep(/$elm2/i, #array1);
if (#matches && (scalar (#matches > 1))) {
print "$elm2,$matches[0], $matches[-1]\n";
}
elsif (#matches) {
print "$elm2,$matches[0]\n";
}
else {print "no matches\n";};
}
a bit late but this is what I think you could use
Find first match
if ($_ =~ m/($element)/) { print $1; }
Find last match
if ($_ =~ m/.*($element)/) { print $1; }
Assuming you want to check which elements of #array2 match any of the patterns in #array and print the first and last of those, it is simplest to build an alternation regex from the contents of #array and filter #array2 using that.
Like this
my $re = join '|', #array; # Build a regex
$re = qr/$re/; # Compile it
my #matches = grep /$re/, #array2;
print "$_\n" for #matches[0,-1];

Perl multi hash from list

I have a list with some values which are connected. I need to create a hashmap with keys and values from the list and merge together. But i don't really know how to do it.
Input:
my #in =(
'mgenv/1_2_3/parent.dx_environment',
'mgenv/1_2_3/doc/types.dat');
Expected output:
"{ $env => { $ver => [ $file1, $file2, ... ] } }"
I've tried these:
(1)
my #sack_files = (
'mgenv/1_2_3/parent.dx_environment',
'mgenv/1_2_3/doc/types.dat');
my $sack_tree = {};
my %hash=();
for( my $i=0; $i<scalar #sack_files; $i++){
my #array = split(/[\/]+/,$sack_files[$i]);
for(my $i=0;$i<(scalar #array)-1;$i++){
my $first = $array[$i];
my $second = $array[$i+1];
$hash{$first}=$second;
}
# merge
}
(2)
use Data::Dumper;
my #sack_files = (
'mgenv/1_2_3/parent.dx_environment',
'mgenv/1_2_3/doc/types.dat',
);
my $sack_tree = {};
my %hash=();
for( my $i=0; $i<scalar #sack_files; $i++){
my #array = split(/[\/]+/,$sack_files[$i]);
nest(\%hash,#array);
}
In the second case I get an error because when the loop variable i=1 ,the key/values already exists so maybe i have to check the previously added key/values. But I don't really know how.
I would really appreciate any ideas.
Just use push to add new members to an existing array in a hash of hashes. You have to dereference the array reference with #{ ... }.
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
my #sack_files = qw( mgenv/1_2_3/parent.dx_environment
mgenv/1_2_3/doc/types.dat
mgenv/1_2_3/doc/etc.dat
mgenv/4_5_6/parent.dx_environment
mgenv/4_5_6/doc/types.dat
u5env/1_2_3/parent.dx_environment
u5env/1_2_3/doc/types.dat
u5env/4_5_6/parent.dx_environment
u5env/4_5_6/doc/types.dat
);
my %hash;
for my $sack_file (#sack_files) {
my ($env, $ver, $file) = split m{/}, $sack_file, 3;
push #{ $hash{$env}{$ver} }, $file;
}
print Dumper \%hash;
output
$VAR1 = {
'mgenv' => {
'1_2_3' => [
'parent.dx_environment',
'doc/types.dat',
'doc/etc.dat'
],
'4_5_6' => [
'parent.dx_environment',
'doc/types.dat'
]
},
'u5env' => {
'4_5_6' => [
'parent.dx_environment',
'doc/types.dat'
],
'1_2_3' => [
'parent.dx_environment',
'doc/types.dat'
]
}
};

perl hash with array

I did same hash like this:
my %tags_hash;
Then I iterate some map and add value into #tags_hash:
if (#tagslist) {
for (my $i = 0; $i <= $#tagslist; $i++) {
my %tag = %{$tagslist[$i]};
$tags_hash{$tag{'refid'}} = $tag{'name'};
}}
But I would like to have has with array, so when key exists then add value to array.
Something like this:
e.g. of iterations
1,
key = 1
value = "good"
{1:['good']}
2,
key = 1
value = "bad"
{1:['good', 'bad']}
3,
key = 2
value = "bad"
{1:['good', 'bad'], 2:['bad']}
And then I want to get array from the key:
print $tags_hash{'1'};
Returns: ['good', 'bad']
An extended example:
#!/usr/bin/perl
use strict;
use warnings;
my $hash = {}; # hash ref
#populate hash
push #{ $hash->{1} }, 'good';
push #{ $hash->{1} }, 'bad';
push #{ $hash->{2} }, 'bad';
my #keys = keys %{ $hash }; # get hash keys
foreach my $key (#keys) { # crawl through hash
print "$key: ";
my #list = #{$hash->{$key}}; # get list associate within this key
foreach my $item (#list) { # iterate through items
print "$item ";
}
print "\n";
}
output:
1: good bad
2: bad
So the value of the hash element to be an array ref. Once you have that, all you need to do is push the value onto the array.
$hash{$key} //= [];
push #{ $hash{$key} }, $val;
Or the following:
push #{ $hash{$key} //= [] }, $val;
Or, thanks to autovivification, just the following:
push #{ $hash{$key} }, $val;
For example,
for (
[ 1, 'good' ],
[ 1, 'bad' ],
[ 2, 'bad' ],
) {
my ($key, $val) = #$_;
push #{ $hash{$key} }, $val;
}

Resources