How create hash with arrays in perl - perl

I have a script that looped over to get the values ​​$mac and $snr. The result of the loop is as follows
133.3133.5132.9133.1132.9131.8234.4233.7234.7230.3232.3230.8331.9332.0331.6331.6330.9330.2 itd.....
9437.39437.09436.79436.89435.19433.09535.29535.
result from $mac is bolt
result form $snr is next
how create %hash where result will be
my %hash = ( mac1 => [’snr1', ‘snr2', ‘snr3', ‘snr4', ‘snr5', ‘snr6'],
mac2 => [’snr1', ‘snr2', ‘snr3', ‘snr4', ‘snr5', ‘snr6'],
mac3 => [’snr1', ‘snr2', ‘snr3', ‘snr4', ‘snr5', ‘snr6'],
);
result with value
my %hash = ( 1 => [’33.3', ‘33.5', ‘32.9', ‘33.1', ‘32.9','31.8' ‘'],
2 => [’34.4', ‘33.7', ‘34.7', ‘30.3', ‘32.3', ‘30.8'],
3 => [’31.9', ‘32.0', ‘31.6', ‘31.6', ‘snr5', ‘snr6'],
);
I can put only part code:
I can put only part code:
my $db_snr = DBI->connect("DBI:mysql:$baza:$host", $user, $pass, { RaiseError=>'0', AutoCommit=>'1'} );
if(! $db_snr){
print LOG "Can't connect to mysql serwer at $host, skipping!\n";
exit 0;
}
foreach $upstrim (sort keys %interface){
$typ = $interface{$upstrim}[50];
$mac=$interface{$upstrim}[0];
$mac=~ s/MAC//;
print($snr)
print($mac) # here I woul like create hash witch array
if(($typ eq 'u') && ($mac =~ m/^\d/)){
my $snr = $interface{$upstrim}[30];
if (($snr < 11) && ($snr > 0)) {
print LOG "Test SNR nadajcy $cmts:$mac:$snr:$typ\n";
$db_snr->do("insert into snr values ('$cmts','$mac',NOW(),NOW(),'$snr','1','0','$upstrim') ON DUPLICATE KEY update snr='$snr' ,data_last=NOW(), licznik=licznik+1 ") or warn "Can't prepare: $DBI::errstr";
}
}
}
$db_snr->disconnect;
close(LOG);

You can just loop over the values, split the line, then use a push statement to add numbers to the hash value, like below. Note that I am using the infile handle DATA for simplicity here, you have to use your own file handle, or the <> operator.
use strict;
use warnings;
use Data::Dumper;
my %hash;
while (<DATA>) {
my ($mac, $snr) = split;
push #{ $hash{$mac} }, $snr;
}
print Dumper \%hash;
__DATA__
1 33,3
1 33.2
1 33.3
1 32.7
1 32.9
1 32.5
1 31.7
2 34.4
2 34.9
2 34.6
2 34.3
2 33.5
2 30.8
3 31.9
3 32.0
3 31.8
3 31.7
3 31.4
3 30.4
95 34.8
96 30.6
96 31.8
96 33.4
96 34.2
96 34.0
96 29.5
Output:
$VAR1 = {
'3' => [
'31.9',
'32.0',
'31.8',
'31.7',
'31.4',
'30.4'
],
'1' => [
'33,3',
'33.2',
'33.3',
'32.7',
'32.9',
'32.5',
'31.7'
],
'2' => [
'34.4',
'34.9',
'34.6',
'34.3',
'33.5',
'30.8'
],
'96' => [
'30.6',
'31.8',
'33.4',
'34.2',
'34.0',
'29.5'
],
'95' => [
'34.8'
]
};

Related

Sorting a hash of arrays using Perl

my %Hash = (
1111 => [5,6] ,
5555 => [3,4],
2222 => [3],
4444 => [3,1]
)
How can i sort hash by values in ascending order, so that all the values should display as sorting but if elements are equal , sorting of keys should be in descending order.
<p> Output:
1111 => 6
5,
5555 => 4
3 ,
4444 => 3
1 ,
2222 => 3
</p>
I don't know what you mean about descending order. Can you show an example? I'll go by the output you show.
I assumed that you didn't want to change the original data (which may not be the case) so I used the map-sort to create an intermediate one. I go through that intermediate result in order and do the fiddly bits to get what you want. Much of the ugliness is to get you the format you wanted.
The postfix dereference is useful here:
use v5.26;
use utf8;
use strict;
use warnings;
my %Hash = (
1111 => [5,6],
5555 => [3,4],
2222 => [3],
4444 => [3,1]
);
foreach my $tuple (
sort {
$b->[1][0] <=> $a->[1][0]
}
map {
[ $_, [ sort { $b <=> $a } $Hash{$_}->#* ] ];
}
keys %Hash
) {
print my $string = "$tuple->[0] => ";
my $indent = length $string;
foreach my $i ( 0 .. $tuple->[1]->$#* ) {
print ' ' x $indent if $i;
print $tuple->[1][$i];
print ',' if $i == $tuple->[1]->$#*;
print "\n";
}
}
This produces:
1111 => 6
5,
5555 => 4
3,
4444 => 3
1,
2222 => 3,

Printing groups of key/value pairs in hash

How can I print a hash in Perl, such that 3 key value pairs are printed on each line?
print %hash;
This will print key value pairs each in a line.
To display the hash, so "that 3[n] key value pairs are printed on each line", you can use a counter ($n) and % (modulo op) to determine when to print a "\n". Demo:
use Modern::Perl;
my %h = ();
for (0..7) {
$h{$_} = chr(65 + $_);
}
print %h, "\n";
my $cols = +$ARGV[0] || 5;
my $n = -$cols;
for my $key (keys %h) {
print $key, ' => ', $h{$key}, 0 == ++$n % $cols ? "\n" : "\t\t";
}
print $n % $cols ? "\n------" : "------";
output:
perl -w 31444449.pl 1
6G4E1B3D0A7H2C5F
6 => G
4 => E
1 => B
3 => D
0 => A
7 => H
2 => C
5 => F
------
perl -w 31444449.pl
6G4E1B3D0A7H2C5F
6 => G 4 => E 1 => B 3 => D 0 => A
7 => H 2 => C 5 => F
------
perl -w 31444449.pl 3
6G4E1B3D0A7H2C5F
6 => G 4 => E 1 => B
3 => D 0 => A 7 => H
2 => C 5 => F
------
Borodin's solutions, however, is simpler.
See mpapec answer for a much improved version.
A very simple way to do this is to copy all the keys and values to an array, and then print six (three pairs) of those at a time
use strict;
use warnings;
my %h = map { $_ => 1 } 'A' .. 'H';
my #kv = %h;
while ( my #row = splice #kv, 0, 6 ) {
print "#row\n";
}
output
B 1 C 1 A 1
D 1 E 1 G 1
F 1 H 1
You can use natatime from List::MoreUtils:
use List::MoreUtils qw/natatime/;
my $it = natatime 6, %ENV;
while (my #vals = $it->()) {
print "#vals\n";
}
List::MoreUtils isn't in core modules, you need to install it.
Thanks All. I tried this and it worked.
my #keylist=sort keys %hash;
my $counter=0;
foreach(#keylist){
#printing the key value pairs
printf "%-15s :%3d ",$_,$hash{$_};
$counter++;
if($counter==3){
$counter=0;
print "\n";
}
}
print "\n";
If you really just want to print hash and check the values for debugging or for analysing then use
use Data::Dumper;
print Dumper(\%hash);
This print hash keys and values at any n number of levels

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'
);

changing a hash value from string to array

data.txt
Name:xyz
ID:1
Value: 1 2 3 4 5 6 7 8 9 ...
ID:2
Value: 9 8 7 6 5 4 3 2 1..
ID:3
Value: 90 89 88....
Name:abc
ID:11
value:...
Intial file.txt
## Header
..
data
data
data
..
Final expected file.txt
## Header xyz_1,xyz_2,xyz_3,abc_11,...
..
data 1 9 90
data 2 8 89
data 3 7 88
data 4 6
..
Current output file.txt
## Header xyz_1,xyz_2,xyz_3,abc_11,...
...
data, 1 2 3 4 5 6 7 8 9 ..,9 8 7 6 5 4 3 2 1 ..,90 89 88
data
data
...
Code
#!/usr/local/bin/perl
use diagnostics;
use strict;
use warnings;
use Tie::File;
my #name_id;
my %test;
#local $/ = '';
open my $fh, '<', 'data.txt' or die "failed: $!";
my %var;
while (<$fh>) {
chomp;
if (m/^([A-Z:]+):\s*(.*)/) {
$var{$1} = $2;
if (exists($var{Name}) && exists($var{ID}) && exists($var{value}) && $1 eq 'value') {
my $var_name = "$var{Name}_$var{ID}";
push #name_id, $var_name;
$test{$var_name} = $var{value};
}
}
}
# print join "\n\t", #test{#name_id};
my $match = "## Header";
tie my #lines, 'Tie::File', 'file.txt' or die "failed : $!";
for my $line (#lines) {
if ($line =~ /^($match.*)/) {
$line = $1 . "," . join ',', #name_id;
}
}
untie #lines;
my $match = "data";
tie my #lines, 'Tie::File', 'file.txt' or die "failed : $!";
my $i = 0;
for my $line (#lines) {
if ($line =~ /^($match.*)/) {
$line = $1 . "," . join(',', map { $test{$_}->[$i] } #name_id);
$i++;
}
}
untie #lines;
Have a problem with this line $line = $1 . "," . join (',', map { $test{$_}->[$i]} #name_id); it throws the error
Can't use string ("1 2 3 4 5 6 7 8 9 .."...) as an ARRAY ref while "strict refs" in use at test.pl line 46, line 80. at test.pl line 46
I think the hash(%test) value I had is a string and I can't split it as an array. Please let me know how to convert it to an array. I tried doing $test{$var_name} = [qw($var{value})]; it didnt work.
You may be interested in this refactoring of your code that seems to do what you want.
#!/usr/local/bin/perl
use strict;
use warnings;
use Tie::File;
open my $fh, '<', 'data.txt' or die "failed: $!";
my #name_id;
my %test;
my %var;
while (<$fh>) {
chomp;
if (my ($key, $val) = /^(\w+):\s*(.*)/) {
$var{$key} = $val;
if ($key eq 'value') {
my $var_name = "$var{Name}_$var{ID}";
push #name_id, $var_name;
$test{$var_name} = [ split ' ', $var{value} ];
}
}
}
tie my #lines, 'Tie::File', 'file.txt' or die "failed : $!";
my $count = 0;
for my $line (#lines) {
if ($line =~ /^## Header/) {
$line .= ' ' . join ',', #name_id;
}
elsif ($line =~ /^data/) {
$line .= ' ' . join ' ', map { $test{$_}[$count] // '' } #name_id;
$count++;
}
}
untie #lines;
output (file.txt)
## Header xyz_1,xyz_2 ,xyz_3
data 1 9 90
data 2 8 89
data 3 7 88
data 4 6
This is surely not right:
$test{$_}->[$i]
Because $test{$_} can only contain a string of some sort.
If you have a string and want to split into an arrayref so the above works, do this:
$test{$var_name} = [split /\s+/, $var{value}];
I have no idea what the code is supposed to accomplish which means that it may run, but I can't tell if it does what it is meant to. The odd variable names (like $test and $var_name didn't help me to understand the purpose).
I'm not too sure I followed your code, but I thought I'd post how to transpose the numbers (unless your code already does that :-) ).
#!/usr/bin/perl
use strict;
use warnings;
my (%data, $name);
while (<DATA>) {
if (/^Name:(.+)/) {
$name = $1
}
elsif (/^Value/) {
# transpose
my $r = 0;
push #{ $data{$name}[$r++] }, $_ for /\d+/g;
}
}
use Data::Dumper; print Dumper \%data;
__DATA__
Name:xyz
ID:1
Value: 1 2 3 4 5 6 7 8 9
ID:2
Value: 9 8 7 6 5 4 3 2 1
ID:3
Value: 90 89 88 87 86 85 84 83 82
Name:abc
ID:11
The dumped results are:
$VAR1 = {
'xyz' => [
[
'1',
'9',
'90'
],
[
'2',
'8',
'89'
],
[
'3',
'7',
'88'
],
[
'4',
'6',
'87'
],
[
'5',
'5',
'86'
],
[
'6',
'4',
'85'
],
[
'7',
'3',
'84'
],
[
'8',
'2',
'83'
],
[
'9',
'1',
'82'
]
]
};

Storing values corresponding to an unique key in perl

I am new to perl. I want to store the values corresponding to the keys in the following fashion. Please see below for a sample input data. Could someone help me to do this in perl.
output:
key value
1 (11, 20)
2 (17, 15)
3 (10, 11)
Input data:
key value
2 17
3 10
1 11
1 20
2 15
3 11
You can store the data in a hash-of-arrays structure (perldoc perldsc):
use warnings;
use strict;
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;
my %data;
while (<DATA>) {
my ($k, $v) = split;
push #{ $data{$k} } , $v;
}
print Dumper(\%data);
=for output
$VAR1 = {
'1' => [
'11',
'20'
],
'2' => [
'17',
'15'
],
'3' => [
'10',
'11'
]
};
=cut
__DATA__
2 17
3 10
1 11
1 20
2 15
3 11
Assuming that each of the lines in the input data is in
a string, use a hash which has unique keys by definition.
If the key exists in the hash push the value onto the arrayref.
If it doesn't exist assign an array reference to the key with the
value. Next time that key appears you'll push the value onto the arrayref.
my $hash = {};
foreach my $line ( #lines ) {
my ($key, $val) = split(/\s/, $line);
if( $hash->{$key} ) {
push( #{ $hash->{ $key } }, $val );
}
else {
$hash->{$key} = [ $val ];
}
}
Using oneliner:
$ echo "key value
2 17
3 10
1 11
1 20
2 15
3 11" | perl -anE'next if 1..1;push#{$h{$F[0]}},$F[1]}{say"key value";$"=", ";say"$_ (#{$h{$_}})"for sort{$a<=>$b}keys%h'
key value
1 (11, 20)
2 (17, 15)
3 (10, 11)