Multiply all values in hash together in Perl? - perl

I would like to multiply all the values of a hash together without having to call the specific elements. E.g. I DON'T want to do $hash{'kv1'} * $hash{'kv2'} * $hash{'kv3'} because I won't know the number of elements in the hash to do this.
I'm guessing there is a simple and efficient function to do this, maybe using each or something, but I can't find any examples to follow. Any ideas?

Start at 1 (since 1 multiplied by anything is what you multiple it by), then loop over the values of the hash and multiple the current total value by the value from the hash.
#!/usr/bin/env perl
use v5.12;
my %hash = ( a => 1, b => 2, c => 3, d => 4 );
my $value = 1;
$value = $value * $_ foreach values %hash;
say $value;

This is what List::Util's reduce is for:
use List::Util 'reduce';
my %hash = (foo => 3, bar => 7, baz => 2);
say reduce { our $a * our $b } values %hash;
Output:
42

I would iterate over keys %hash like this:
my $prod = 1;
$prod *= $hash{$_} for keys %hash;

will this not do?
my $m = 1;
for (values %my_hash) {
$m *= $_;
}

Related

I need help regarding perl hash of array

I have a hash of array and two variables -1 list and 1-scalar value
How do I do this?
I want two things out of this. First is a list for every key.
Second is I need $b to have the value of last array element of every key
%abc=(
0=>[1,2,3],
1=>[1,5]
);
#a;
$b;
for key 0 i need #a to have [1,2] and for key 1 i need #a to have [1].
for 0 key i need $b to have value 3 and for key 1 i need $b to have value 5
I understand you want #a to hold all values but last, and $b to just hold the last value. How about:
use feature 'say';
my %abc = (0 => [1,2,3], 1 => [1,5]);
for my $key (keys %abc) {
my #a = #{$abc{$key}};
my $b = pop #a;
say "#a / $b"
}
I'd almost certainly use something like the other solution here. But here's a different solution that a) uses values() instead of keys() and b) uses reverse() to simplify(?!) the assignments. Don't do this :-)
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
my %abc = (0 => [1,2,3], 1 => [1,5]);
for (values %abc) {
my ($b, #a) = reverse #$_;
#a = reverse #a;
say "#a / $b";
}

Adding lists of key/values to an existing hash

I can initialize a hash from a list, e.g:
my %hash = (1..10);
my %hash2 = split /[\n=]/;
Is there a better (briefer) way to add lists of new key/values to a hash than using temporary variables?
while (<>) {
my ($k, $v) = split /=/, $_, 2;
$hash{$k} = $v;
}
It's possible, yes. It's rather like adding some numbers to an existing total - you might accomplish that like this:
my $total = 1 + 2 + 3; # here's the existing total
# let's add some additional numbers
$total = $total + 4 + 5; # the $total variable appears both
# left and right of the equals sign
You can do similar when inserting values to an existing hash...
my %hash = (a => 1, b => 2); # here's the existing hash.
# let's add some additional values
$_ = "c=3" . "\n" . "d=4";
%hash = (%hash, split /[\n=]/); # the %hash variable appears both
# left and right of the equals sign
With addition, there's a nice shortcut for this:
$total += 4 + 5;
With inserting values to a hash, there's no such shortcut.
Maybe I'm wrong but the next
use 5.014;
use warnings;
use Data::Dumper;
#create a hash
my %hash = map { "oldvar" . $_ => "oldval" . $_ } (1..3);
#add to the hash
%hash = (%hash, map {split /[=\n]/} <DATA>);
say Dumper \%hash;
__DATA__
var1=val1
var2=val2
var3=val3
prints
$VAR1 = {
'oldvar1' => 'oldval1',
'oldvar2' => 'oldval2',
'var1' => 'val1',
'var3' => 'val3',
'oldvar3' => 'oldval3',
'var2' => 'val2'
};

sum hash of hash values using perl

I have a Perl script that parses an Excel file and does the following : It counts for each value in column A, the number of elements it has in column B, the script looks like this :
use strict;
use warnings;
use Spreadsheet::XLSX;
use Data::Dumper;
use List::Util qw( sum );
my $col1 = 0;
my %hash;
my $excel = Spreadsheet::XLSX->new('inout_chartdata_ronald.xlsx');
my $sheet = ${ $excel->{Worksheet} }[0];
$sheet->{MaxRow} ||= $sheet->{MinRow};
my $count = 0;
# Iterate through each row
foreach my $row ( $sheet->{MinRow}+1 .. $sheet->{MaxRow} ) {
# The cell in column 1
my $cell = $sheet->{Cells}[$row][$col1];
if ($cell) {
# The adjacent cell in column 2
my $adjacentCell = $sheet->{Cells}[$row][ $col1 + 1 ];
# Use a hash of hashes
$hash{ $cell->{Val} }{ $adjacentCell->{Val} }++;
}
}
print "\n", Dumper \%hash;
The output looks like this :
$VAR1 = {
'13' => {
'klm' => 1,
'hij' => 2,
'lkm' => 4,
},
'12' => {
'abc' => 2,
'efg' => 2
}
};
This works great, my question is : How can I access the elements of this output $VAR1 in order to do : for value 13, klm + hij = 3 and get a final output like this :
$VAR1 = {
'13' => {
'somename' => 3,
'lkm' => 4,
},
'12' => {
'abc' => 2,
'efg' => 2
}
};
So basically what I want to do is loop through my final hash of hashes and access its specific elements based on a unique key and finally do their sum.
Any help would be appreciated.
Thanks
I used #do_sum to indicate what changes you want to make. The new key is hardcoded in the script. Note that the new key is not created if no key exists in the subhash (the $found flag).
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
my %hash = (
'13' => {
'klm' => 1,
'hij' => 2,
'lkm' => 4,
},
'12' => {
'abc' => 2,
'efg' => 2
}
);
my #do_sum = qw(klm hij);
for my $num (keys %hash) {
my $found;
my $sum = 0;
for my $key (#do_sum) {
next unless exists $hash{$num}{$key};
$sum += $hash{$num}{$key};
delete $hash{$num}{$key};
$found = 1;
}
$hash{$num}{somename} = $sum if $found;
}
print Dumper \%hash;
It sounds like you need to learn about Perl References, and maybe Perl Objects which are just a nice way to deal with references.
As you know, Perl has three basic data-structures:
Scalars ($foo)
Arrays (#foo)
Hashes (%foo)
The problem is that these data structures can only contain scalar data. That is, each element in an array can hold a single value or each key in a hash can hold a single value.
In your case %hash is a Hash where each entry in the hash references another hash. For example:
Your %hash has an entry in it with a key of 13. This doesn't contain a scalar value, but a references to another hash with three keys in it: klm, hij, and lkm. YOu can reference this via this syntax:
${ hash{13} }{klm} = 1
${ hash{13} }{hij} = 2
${ hash{13} }{lkm} = 4
The curly braces may or may not be necessary. However, %{ hash{13} } references that hash contained in $hash{13}, so I can now reference the keys of that hash. You can imagine this getting more complex as you talk about hashes of hashes of arrays of hashes of arrays. Fortunately, Perl includes an easier syntax:
$hash{13}->{klm} = 1
%hash{13}->{hij} = 2
%hash{13}->{lkm} = 4
Read up about hashes and how to manipulate them. After you get comfortable with this, you can start working on learning about Object Oriented Perl which handles references in a safer manner.

Perl map - need to map an array into a hash as arrayelement->array_index

I have a array like this:
my #arr = ("Field3","Field1","Field2","Field5","Field4");
Now i use map like below , where /DOSOMETHING/ is the answer am seeking.
my %hash = map {$_ => **/DOSOMETHING/** } #arr
Now I require the hash to look like below:
Field3 => 0
Field1 => 1
Field2 => 2
Field5 => 3
Field4 => 4
Any help?
%hash = map { $arr[$_] => $_ } 0..$#arr;
print Dumper(\%hash)
$VAR1 = {
'Field4' => 4,
'Field2' => 2,
'Field5' => 3,
'Field1' => 1,
'Field3' => 0
};
my %hash;
#hash{#arr} = 0..$#arr;
In Perl 5.12 and later you can use each on an array to iterate over its index/value pairs:
use 5.012;
my %hash;
while(my ($index, $value) = each #arr) {
$hash{$value} = $index;
}
Here's one more way I can think of to accomplish this:
sub get_bumper {
my $i = 0;
sub { $i++ };
}
my $bump = get_bumper; # $bump is a closure with its very own counter
map { $_ => $bump->(); } #arr;
As with many things that you can do in Perl: Don't do this. :) If the sequence of values you need to assign is more complex (e.g. 0, 1, 4, 9, 16... or a sequence of random numbers, or numbers read from a pipe), it's easy to adapt this approach to it, but it's generally even easier to just use unbeli's approach. The only advantage of this method is that it gives you a nice clean way to provide and consume arbitrary lazy sequences of numbers: a function that needs a caller-specified sequence of numbers can just take a coderef as a parameter, and call it repeatedly to get the numbers.
A very old question, but i had the same problem and this is my solution:
use feature ':5.10';
my #arr = ("Field3","Field1","Field2","Field5","Field4");
my %hash = map {state $i = 0; $_ => $i++} #arr;

How can I get a only part of a hash in Perl?

Is there a way to get a sub-hash? Do I need to use a hash slice?
For example:
%hash = ( a => 1, b => 2, c => 3 );
I want only
%hash = ( a => 1, b => 2 );
Hash slices return the values associated with a list of keys. To get a hash slice you change the sigil to # and provide a list of keys (in this case "a" and "b"):
my #items = #hash{"a", "b"};
Often you can use a quote word operator to produce the list:
my #items = #hash{qw/a b/};
You can also assign to a hash slice, so if you want a new hash that contains a subset of another hash you can say
my %new_hash;
#new_hash{qw/a b/} = #hash{qw/a b/};
Many people will use a map instead of hash slices:
my %new_hash = map { $_ => $hash{$_} } qw/a b/;
Starting with Perl 5.20.0, you can get the keys and the values in one step if you use the % sigil instead of the # sigil:
my %new_hash = %hash{qw/a b/};
You'd probably want to assemble a list of keys you want:
my #keys = qw(a b);
And then use a loop to make the hash:
my %hash_slice;
for(#keys) {
$hash_slice{$_} = %hash{$_};
}
Or:
my %hash_slice = map { $_ => $hash{$_} } #keys;
(My preference is the second one, but whichever one you like is best.)
Yet another way:
my #keys = qw(a b);
my %hash = (a => 1, b => 2, c => 3);
my %hash_copy;
#hash_copy{#keys} = #hash{#keys};
Too much functional programming leads me to think of zip first.
With List::MoreUtils installed,
use List::MoreUtils qw(zip);
%hash = qw(a 1 b 2 c 3);
#keys = qw(a b);
#values = #hash{#keys};
%hash = zip #keys, #values;
Unfortunately, the prototype of List::MoreUtils's zip inhibits
zip #keys, #hash{#keys};
If you really want to avoid the intermediate variable, you could
zip #keys, #{[#hash{#keys}]};
Or just write your own zip without the problematic prototype. (This doesn't need List::MoreUtils at all.)
sub zip {
my $max = -1;
$max < $#$_and $max = $#$_ for #_;
map { my $ix = $_; map $_->[$ix], #_; } 0..$max;
}
%hash = zip \#keys, [#hash{#keys}];
If you're going to be mutating in-place,
%hash = qw(a 1 b 2 c 3);
%keep = map +($_ => 1), qw(a b);
$keep{$a} or delete $hash{$a} while ($a, $b) = each %hash;
avoids the extra copying that the map and zip solutions incur. (Yes, mutating the hash while you're iterating over it is safe... as long as the mutation is only deleting the most recently iterated pair.)
FWIW, I use Moose::Autobox here:
my $hash = { a => 1, b => 2, c => 3, d => 4 };
$hash->hslice([qw/a b/]) # { a => 1, b => 2 };
In real life, I use this to extract "username" and "password" from a form submission, and pass that to Catalyst's $c->authenticate (which expects, in my case, a hashref containing the username and password, but nothing else).
New in perl 5.20 is hash slices returning keys as well as values by using % like on the last line here:
my %population = ('Norway',5000000,'Sweden',9600000,'Denmark',5500000);
my #slice_values = #population{'Norway','Sweden'}; # all perls can do this
my %slice_hash = %population{'Norway','Sweden'}; # perl >= 5.20 can do this!
A hash is an unordered container, but the term slice only really makes sense in terms of an ordered container. Maybe look into using an array. Otherwise, you may just have to remove all of the elements that you don't want to produce your 'sub-hash'.