perl, build strings from several variables content - perl

Here is my starting point:
#!/usr/bin/env perl
use warnings;
use strict;
my %hash_a = (
"num" => 7,
"date" => 20221104,
"prath" => "1.1.10",
"antema" => "1.1.15" );
my %hash_b = (
"num" => 8,
"date" => 20221105,
"prath" => "1.1.16",
"antema" => "1.1.19" );
my %hash_c = (
"num" => 9,
"date" => 20221112,
"prath" => "1.1.20",
"antema" => "1.1.39" );
from this I want to make these strings using a loop, if possible without using any trick like building variable names through a loop to get 'hash_a', 'hash_b', 'hash_c'. I was used to use multidimensional arrays for such things in python.
07_class_date_20221104_-_starting_verse_1.1.10_-_closing_verse_1.1.15.mp4
08_class_date_20221105_-_starting_verse_1.1.16_-_closing_verse_1.1.19.mp4
08_class_date_20221112_-_starting_verse_1.1.20_-_closing_verse_1.1.39.mp4

I take it your question is more about looping over the variables, and not so much about building the string.
Usually you would not make a bunch of one record hashes and then try to loop over them, like you are doing with %hash_a, %hash_b etc. I would put them all in a single structure, in this case an array:
my #all = (
{
"num" => 7,
"date" => 20221104,
"prath" => "1.1.10",
"antema" => "1.1.15"
},
{
"num" => 8,
"date" => 20221105,
"prath" => "1.1.16",
"antema" => "1.1.19"
},
{
"num" => 9,
"date" => 20221112,
"prath" => "1.1.20",
"antema" => "1.1.39"
});
Then you can simply loop over the array:
for my $record (#all) {
my $num = $record->{num}; # etc...
And build your string with sprintf

Here is an example:
use feature qw(say);
use strict;
use warnings;
use experimental qw(declared_refs refaliasing);
my %hash_a = (
"num" => 7,
"date" => 20221104,
"prath" => "1.1.10",
"antema" => "1.1.15"
);
my %hash_b = (
"num" => 8,
"date" => 20221105,
"prath" => "1.1.16",
"antema" => "1.1.19"
);
my %hash_c = (
"num" => 9,
"date" => 20221112,
"prath" => "1.1.20",
"antema" => "1.1.39"
);
sub get_str {
my \%hash = $_[0];
sprintf "%02d_class_date_%s_-_starting_verse_%s_-closing_verse_%s.mp4",
$hash{num}, $hash{date}, $hash{prath}, $hash{antema};
}
for my $ref (\%hash_a, \%hash_b, \%hash_c) {
my $str = get_str($ref);
say $str;
}
Output:
07_class_date_20221104_-_starting_verse_1.1.10_-closing_verse_1.1.15.mp4
08_class_date_20221105_-_starting_verse_1.1.16_-closing_verse_1.1.19.mp4
09_class_date_20221112_-_starting_verse_1.1.20_-closing_verse_1.1.39.mp4

Related

how to create reference to a value of a subhash member in Perl?

I would like to use reference to make the code shorter
I make it simple, only one level of depth here :
my %cx = ( 'a' => ( "A" => 7, "B" => 8), 'b' => ( "Z" => 20 ));
# I want a ref to the B's value for testing, with a possible increment action :
my $ref = \$cx{a}{B}; # so I just put a \ before
if ($$ref and $$ref < 10) { $$ref ++; } # will give $cx{a}{B} = 9
# I have the same need for inner references, for example :
my $ref = \$stock{$stockName}->{places}->{$otherHashRef->{andItsKey}}
But this doesn't work
Parens don't contruct anything; they just change precedence. So,
my %cx = ( 'a' => ( "A" => 7, "B" => 8), 'b' => ( "Z" => 20 ));
is just a weird way of writing
my %cx = ( 'a' => "A", 7 => "B", 8 => 'b', "Z" => 20 );
Curlies create a hash and returns a reference to a hash, so you want
my %cx = ( 'a' => { "A" => 7, "B" => 8 }, 'b' => { "Z" => 20 });
For example,
$ perl -MData::Dumper -e'
{
my %cx = ( 'a' => { "A" => 7, "B" => 8 }, 'b' => { "Z" => 20 });
my $ref = \$cx{a}{B};
++$$ref;
print(Dumper(\%cx));
}
{
my %stock;
my $stockName = "abc";
my $otherHashRef = { andItsKey => 'def' };
my $ref = \$stock{$stockName}->{places}->{$otherHashRef->{andItsKey}};
++$$ref;
print(Dumper(\%stock));
}
'
$VAR1 = {
'a' => {
'A' => 7,
'B' => 9
},
'b' => {
'Z' => 20
}
};
$VAR1 = {
'abc' => {
'places' => {
'def' => 1
}
}
};

Declare hash variable in loop

I need to use a hash and loop in my code. Please see the sample code it's not working. i wanted to print the variable wafer, site and res side by side so it will look like this
1, 1, 63
1, 2, -53
1, 3, 9.47
1, 4, 9.55
1, 5, -8.32
my #wafer = ("1","1","1","1","1");
my #site = ("1", "2", "3", "4", "5");
my #res = ("63","-53","9.47","9.55","-8.32");
my %hash;
foreach my $result(#res) {
$hash{$wafer[0]}{$site[0]} = $result;
last;
}
print "$wafer{$wafer[0]}{$site[0]} \n";
When you want to iterate several arrays synchronously, iterate over the indices:
for my $index (0 .. $#wafer) {
print "$wafer[$index] $site[$index] $res[$index]\n";
}
You also might want to build a hash keyed by the site (as it's the only unique value):
for my $index (0 .. $#wafer) {
$hash{ $site[$index] } = { wafer => $wafer[$index],
res => $res[$index] };
}
This will create a hash like this:
%hash = (
'4' => {
'res' => '9.55',
'wafer' => '1'
},
'3' => {
'wafer' => '1',
'res' => '9.47'
},
'1' => {
'res' => '63',
'wafer' => '1'
},
'2' => {
'res' => '-53',
'wafer' => '1'
},
'5' => {
'res' => '-8.32',
'wafer' => '1'
}
);

sorting keys (which are also values) on the basis of values in hashes in perl

For example, this is my hash
'Level1_A' => {
'Level2_A' => 1071,
'Level2_B' => 3429,
'Level2_C' => 3297
},
'Level1_B' => {
'Level2_A' => 702,
'Level2_B' => 726
},
And I want an output which should look like
'Level1_A' => {
'Level2_B' => 3429,
'Level2_C' => 3297,
'Level2_A' => 1071
},
'Level1_B' => {
'Level2_B' => 726,
'Level2_A' => 702
},
Ultimately, I want to the keys corresponding to the highest values.
Level1_A___Level2_B___3429
Level1_B___Level2_A____726
Hashes are inherently unordered and there is no way to sort them. However, you can find the maximum of the values of a hash and it is best to use a module to help
List::UtilsBy provides max_by and will allow you to find the hash key corresponding to the maximum numeric value
Like this
use strict;
use warnings 'all';
use List::UtilsBy 'max_by';
my $data = {
'Level1_A' => {
'Level2_A' => 1071,
'Level2_B' => 3429,
'Level2_C' => 3297,
},
'Level1_B' => {
'Level2_A' => 702,
'Level2_B' => 726
},
};
for my $k1 ( sort keys %$data ) {
my $v1 = $data->{$k1};
my $k2 = max_by { $v1->{$_} } keys %$v1;
printf "%s %s %s\n", $k1, $k2, $v1->{$k2};
}
output
Level1_A Level2_B 3429
Level1_B Level2_B 726

How to dig into an certain hash depth?

I have a hash where I don't know its depth. I got it with DBI::selectall_hashref where the second parameter is given by the user.
So depending on the query I can have something like this for a 2-levels hash.
hash_ref = (
aphrodite => (
foo => (
name => aphrodite,
foobar => foo
a => 1,
b => 2,
)
bar => (
name => aphrodite,
foobar => bar
a => 1,
b => 2,
)
)
apollo => (
...
)
ares => (
...
)
)
As you can see the key columns are redundant into the hash. I would like to remove the redundant keys.
If I know that this is a 2-levels hash I can easily solve my problem with this:
for my $name (keys $hash_ref) {
for my $foobar (keys $hash_ref->{$name}) {
my $h = $hash_ref->{$name}{$foobar};
delete $h->{name};
delete $h->{foobar};
}
}
However with a 3-levels hash I will need 3 cascaded for-loop and so on.
How can I dynamically remove the redundant keys from $hash_ref i.e. name and foobar?
My initial idea was to recursively iterate trough my hash:
iterate($hash_ref, scalar #keys);
sub iterate {
my ($ref, $depth) = #_;
for(keys $ref) {
if ($depth > 0) {
iterate($ref->{$_}, $depth - 1);
}
else {
delete $ref->{$_} for(#keys);
}
}
}
It works but It's ugly, very ugly... Before going any further I would like to know if I missed something. Perhaps the solution could be much simpler that I think.
Any ideas?
More details?
I am writing a database fetcher that takes a user configuration that contains the SQL query $sql and the hash keys #keys. So I get the values from the database with:
$dbh->selecthall_hashref($sql, \#keys, {}, #bind);
I also have to clean fetched data according to additional. Do apply these rules, I have to iterate into the deepest level of $hash_ref to access the keys/values.
I think this does what you need. Essentially it recurses through the hash until it finds a layer where the hash values aren't references. Then it removes the elements from that layer with the keys in #keys
use strict;
use warnings;
use 5.010;
use Data::Dump;
use List::Util 'any';
my $hash_ref = {
aphrodite => {
bar => { name => "aphrodite", foobar => "bar", a => 3, b => 4, },
foo => { name => "aphrodite", foobar => "foo", a => 1, b => 2, },
},
apollo => {
bar => { name => "apollo", foobar => "bar", a => 7, b => 8, },
foo => { name => "apollo", foobar => "foo", a => 5, b => 6, },
},
ares => {
bar => { name => "ares", foobar => "bar", a => 11, b => 12, },
foo => { name => "ares", foobar => "foo", a => 9, b => 10, },
},
};
my #keys = qw/ name foobar /;
remove_dups($hash_ref, \#keys);
dd $hash_ref;
sub remove_dups {
my ($href, $keys) = #_;
if ( any { ref } values %$href ) {
remove_dups($_, $keys) for values %$href;
}
else {
delete #{$href}{#$keys};
}
}
output
{
aphrodite => { bar => { a => 3, b => 4 }, foo => { a => 1, b => 2 } },
apollo => { bar => { a => 7, b => 8 }, foo => { a => 5, b => 6 } },
ares => { bar => { a => 11, b => 12 }, foo => { a => 9, b => 10 } },
}

Perl push a key before a hash data set

I managed to create a hash data set with a subroutine,
my %check_ip = (
"data1" => $ip1,
"data2" => $ip2,
"data3" => $ip3
);
'data1' => '127.0.0.1',
'data2' => '192.168.0.1',
'data3' => '192.168.1.1'
This is a simple hash.
I am looking to put another key behind this, so that this would become a hash of hash, and look like
config1 =>
'data1' => '127.0.0.1',
'data2' => '192.168.0.1',
'data3' => '192.168.1.1',
What is the best way to do this?
To create a nested hash, you need a hash reference.
my %check_ip = (
data1 => $ip1,
data2 => $ip2,
data3 => $ip3,
);
my %config = ( config1 => \%check_ip );
#!/usr/bin/perl
use strict;
use warnings;
my $ip1='127.0.0.1';
my $ip2='192.168.0.1';
my $ip3='192.168.1.1';
my %check_ip = (
config1 => { "data1" => $ip1,
"data2" => $ip2,
"data3" => $ip3, },
);
Access like below:
print $check_ip{config1}{data1}; #output 127.0.0.1
Demo
Since a hash key can only have one value, the nested hash needs to be stored as a hash reference, which is what the curly braces {} are used for:
my %check_ip = (
config1 => { "data1" => $ip1,
"data2" => $ip2,
"data3" => $ip3, },
);
See perldoc perldsc for more information on Perl data structures.