I know that there have been multiple posts about this topic already, but I have not found them to be helpful for my particular case.
When I run this excerpt of code
my $bandName = '';
my $rrh_sth = $dbh->prepare("SELECT serial....");
$rrh_sth->execute;
my $int_hash = $rrh_sth->fetchall_hashref('serial');
delete $_->{serial} for values %$int_hash;
foreach my $s ( keys %$int_hash ) {
if ( $values[6] eq $s ) {
#$bandName = #{%$int_hash}{$s};
$bandName = $int_hash ->{$s};
my $ulink = getFreq($bandName, "u");
my $dlink = getFreq($bandName, "d");
my $status = "active";
print Dumper (
$bandName,
$values[0],
$values[8],
$values[7],
$values[6],
$values[11],
$values[10],
$values[3],
$values[4],
$values[5],
$dlink,
$ulink
);
}
}
I keep getting the key and the value for the particular serial, represented by $s. I know that I have to de-reference int_hash, but I am not sure how now that the methods that I have tried do not work.
$VAR1 = {
'rrhName' => 'MCD59'
};
$VAR2 = 'MCO57';
$VAR3 = 'R-S-S:1-0-1';
$VAR4 = 'scb';
$VAR5 = '12NT46000050';
$VAR6 = undef;
$VAR7 = '109786038';
$VAR8 = 'MCO: 976 2x5W AC';
$VAR9 = 'enabled';
$VAR10 = '1';
$VAR11 = ''; #EDITED
$VAR12 = ''; #EDITED
In general, when I have a reference, I need to dereference it to get to the slices, but in the case of $VAR1, when I go to my database to see the value being inserted its telling me the position of $VAR1 in memory, which is not what I thought I was asking for.
Your $int_hash is a hash reference, and you dereference it correctly into a hash, %$int_hash, to then retrieve its keys. The obtained key is then also used correctly to get the corresponding value, $int_hash->{$s}.
However, as the Dumper output shows, the value associated with the key $s is itself a hash reference. Since a reference itself is a scalar we can always assign it as a value to a key, which is how complex (nested) data structures are built. See perldsc for a cookbook and for references in general see perlreftut for a tutorial and perlref for a reference.
In short, you only need to keep dereferencing
$int_hash->{$s}->{'rrhName'}; # or,
$int_hash->{$s}{'rrhName'}; # or even
$int_hash->{$s}{rrhName};
The second and third lines are syntax shortcuts, explained in the above pages.
Either of these will return the value in the nested hash, MCD59.
You are committing the sin of doing a linear search for a key in the hash %$int_hash that matches $values[6]. You don't access arrays that way. It would look like this
my $values_6;
for my $i ( 0 .. $#values ) {
if ( $i == 6 ) {
$values_6 = $values[$i];
}
}
You need to discard your for loop and simply access the hash element directly. Combining that with accessing the value of the second-level hash, your code should look like this
my $band_name;
if ( my $band = $int_hash->{$values[6]} ) {
$band_name = $band->{rrhName};
my $ulink = getFreq($band_name, 'u');
my $dlink = getFreq($band_name, 'd');
my $status = 'active';
}
Related
I am interested only to know the value of key "DelayedAutoStart" and expect this code to work, but it print much more information. Can someone tell me what is wrong here?
use Win32::Registry;
use Data::Dumper;
$p = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
$p = "SYSTEM\\CurrentControlSet\\Services\\sppsvc";
$main::HKEY_LOCAL_MACHINE->Open($p, $CurrVer) ||
die "Open: $!";
$CurrVer->GetValues(\%vals);
#print Dumper(\%vals);
foreach $k (keys %vals) {
$key = $vals{$k};
if ($$key[0] == "DelayedAutoStart")
{
print "$$key[0] = $$key[2]\n";
}
}
RESULT:
ServiceSidType = 1
ErrorControl = 1
LaunchProtected = 1
DisplayName = #%SystemRoot%\system32\sppsvc.exe,-101
Start = 2
ImagePath = %SystemRoot%\system32\sppsvc.exe
Description = #%SystemRoot%\system32\sppsvc.exe,-100
DelayedAutoStart = 1
ObjectName = NT AUTHORITY\NetworkService
RequiredPrivileges = SeAuditPrivilege SeChangeNotifyPrivilege
SeCreateGlobalPrivilege SeImpersonatePrivilege
FailureActions = ÇQ☺ ♥ ¶ ☺ └È☺ ☺ Óô♦︎
DependOnService = RpcSs
Type = 16
Please add use strict and use warnings to your code. They will give you an error telling you that you're using the wrong kind of comparison operator. The == equality operator is for comparing numbers not strings. You need eq which does a string comparison.
Also, you're confusing matters rather by storing a hash value in a variable called $key and generally making things far more complicated than they need to be!
foreach my $key (keys %vals) {
if ($key eq "DelayedAutoStart")
{
print "$key = $vals{$key}[2]\n";
}
}
But, of course, you can just look up the value in the hash directly. No need to iterate over the keys. That's pretty much the point of using a hash :-)
my $key = 'DelayedAutoStart';
if (exists $vals{$key}) {
print "$vals{$key} = $vals{$key}[2]\n";
}
My input files have percentage exposures and I read in only the highest value.
I keep getting errors
odd number of elements in anonymous hash
and
Can't use string as an ARRAY ref while "strict refs" in use
I tried forcing the numbers to be read in as integers but fell flat. Any advice? I'd like to create a hash with key and highest value.
while ( <$DATA_FILE> ) {
chomp;
my #line_values = split( /,/, $_ );
my $state_id = $line_values[0];
# skip header row
next if ( $state_id eq $HEADER_VALUE );
for ( #line_values ) {
tr/%%//d
};
# assign data used as hash keys to variables
my $var1 = int( $line_values[1] );
my $var2 = int( $line_values[2] );
if ( $var1 > $var2 ) {
%report_data = ( { $state_id } => { \#$var1 } )
}
else {
%report_data = ( { $state_id } => { \#$var2 } )
}
} # end while
print \%report_data;
# close file
close( $DATA_FILE ) || printf( STDERR "Failed to close $file_path\n" );
}
It's hard to be sure without any indication of what the input and expected output should be, but at a guess your if blocks should look like this
if ( $var1 > $var2 ) {
$report_data{$state_id} = $var1;
}
else {
$report_data{$state_id} = $var2;
}
or, more simply
$report_data{$state_id} = $var1 > $var2 ? $var1 : $var2;
This line is the culprit:
%report_data = ({$state_id} => {\#$var1})
{ } creates a hashref. You are doing two weird things in this line: use a hashref with a single key ($state_id) as the key in %report_data and a hashref with a single key (\#$var1, which tries to derefence the scalar $var1 and use it in array context (#$var1) and then tries to turn that into a reference again (I'm confused, no wonder the interpreter is as well) ).
That'd make more sense as
%report_data = ($state_id => $var1);
But that would reset the hash %report_data for every line you read.
What you want is to just set the key of that hash, so first, define the variable before the loop:
my %report_data = ();
and in your while loop, just set a key to a value:
$report_data{ $state_id } = $var1; # or $var2
Finally, you are trying to print a reference to the hash, which makes little sense. I'd suggest a simple loop:
for my $key (keys %report_data) {
print $key . " = " . $report_data{ $key } . "\n";
}
This iterates over all the keys in the hash and then prints them with their values, and looks easy to read and understand, I hope.
In general: don't try to use references unless you know how they work. They are not a magic bullet that you fire at your code and all the problems go poof; quite the opposite, they can become that painful bullet that ends up in your own foot. It's good to learn about references, though, because you will need them when you advance and work more with Perl. perlreftut is a good place to start.
I'm doing some exercises to increase my Perl skills and one of them involves connecting to an SQL database, running a query, and returning the results as an array. This is what I have so far:
my $search = $_[0];
our $dbh = DBI->connect{"dbi:mysql:dbname=database", "root", "password")
or die $DBI::errstr;
my $sql = $dbh->selectall_arrayref("SELECT Player from Players_Sport where Sport like '$search'")
or die $DBI::errstr;
my #array = map { $_->[0] } #$sql;
$dbh->disconnect
or warn "Disconnection failed: $DBI::errstr\n";
return #array;
My next step is to change the code to return the results as a hash instead of an array, but I'm not certain how to proceed. I expect to use selectall_hashref, but all the online examples I've found use WHILE. I want to return the results, then create the hash as separate steps.
Using the selectall_hashref method combines prepare, execute and fetchall_arrayref into a single call. It returns a reference to an array containing a reference to an hash for each row of data fetched. To addon to previous comments of responses.
A verbose way of turning your selectall_arrayref call in to a similar hash where ID is your primary key, you could do something like this.
my %rows = ();
for my $i ( 0..$#{$sql} ) {
my ($id, $player, $sport) = #{$sql->[$i]};
$rows{$id} = [ $player, $sport ];
}
And now to access them:
foreach ( sort(keys(%rows)) ) {
print "$_, -> $rows{$_}->[0], -> $rows{$_}->[1]\n";
}
Now using selectall_hashref, your call would be something like this. ID is the primary key here.
my $href = $dbh->selectall_hashref( q/SELECT ID, Player, Sport
FROM Players_Sport/, q/ID/ );
To access your keys you can use in many ways, here is an example:
foreach ( keys %$href ) {
print "$_, $href->{$_}->{Player}, $href->{$_}->{Sport}\n";
}
Your primary key is placed as $_, your individual keys you can be access out of the hash by saying.
$href->{$_}->{Player}
Again it is just like saying:
foreach my $i ( keys %$href ) {
print "$i, $href->{$i}->{Player}, $href->{$i}->{Sport}\n";
}
Each item is looping through $i
When you use selectall_hashref you must tell it what column to use as the unique key. Specify a column you know will have unique values for every row, then it will populate a hash with 'row' objects using the key column values as the key. Each 'row' is itself a hashref with the column names as the keys.
From the DBI page:
$hash_ref = $dbh->selectall_hashref($statement, $key_field);
$hash_ref = $dbh->selectall_hashref($statement, $key_field, \%attr);
$hash_ref = $dbh->selectall_hashref($statement, $key_field, \%attr, #bind_values);
In all three versions you provide $key_field to identify the column that will be used as the hash key.
Hashes define an association between 2 things, but what is the key, or value in this case? Assuming that you want a 1 as the value:
my %hash = map { $_, 1 } #array;
or,
my %hash = map { $_->[0], 1 } #$sql;
How can I Initialise and clear multiple hash in one line.
Ex:
my %hash1 = ();
my %hash2 = ();
my %hash3 = ();
my %hash4 = ();
to
my ( %hash1, %hash2, %hash3, %hash4 ) = ?
It appears (from your comments) that you really want to empty hashes that already have stuff in them. You can do it like this:
(%hash1,%hash2,%hash3) = ();
Complete example:
use strict;
use warnings;
my %hash1 = ('foo' => 1);
my %hash2 = ('bar' => 1);
my %hash3 = ('baz' => 1);
(%hash1,%hash2,%hash3) = ();
print (%hash1,%hash2,%hash3);
A variable declaration always gives you an empty variable, so there is no need to set it to empty. This is true even in a loop:
for (0..100)
{
my $x;
$x++;
print $x;
}
This will print 1 over and over; even though you might expect $x to retain its value, it does not.
Explanation: Perl allows list assignment like ($foo,$bar) = (1,2). If the list on the right is shorter, any remaining elements get assigned undef. Thus assigning the empty list to a list of variables makes them all undefined.
Another useful way to set a bunch of things is the x operator:
my ($x,$y,$z) = (100)x3;
This sets all three variables to 100. It doesn't work so well for hashes, though, because each one needs a list assigned to it.
It's as simple as doing
my ( %hash1, %hash2, %hash3, %hash4 );
and they will not contain any keys or values at that point.
The same technique applies to scalars and arrays.
To undef multiple hashes, you could do
undef %$_ for ( \%hash1, \%hash2 );
You can initialize it as:
my %hash1 = %hash2 = %hash3 = %hash4 = ();
You do not need to assign anything to a new variable in order to assure it is empty. All variables are empty, if nothing has been assigned to them.
my %hash; # hash contains nothing
%hash = () # hash still contains nothing
The only time it would be useful to assign the empty list to a hash is if you want to remove previously assigned values. And even then, that would only be a useful thing to do if it could not already be solved by applying the correct scope restriction to the hash.
my (%hash1, %hash2);
while (something) {
# some code
(%hash1,%hash2) = (); # delete old values
}
Emptying the hashes. Better written as:
while (something) {
my (%hash1, %hash2); # all values are now local to the while loop
# some code
}
Can a Hash have duplicate keys or values?
it can have duplicate values but not keys.
For both hashes and arrays, only one scalar can be stored at a given key. ("Keys are unique.") If they weren't, you couldn't do
$h{a} = 1;
$h{a} = 2;
$val = $h{a}; # 2
$a[4] = 1;
$a[4] = 2;
$val = $a[4]; # 2
If you wanted to associate multiple values with a key, you could place a reference to an array (or hash) at that key, and add the value to that array (or hash).
for my $n (4,5,6,10) {
if ($n % 2) {
push #{ $nums{odd} }, $n;
} else {
push #{ $nums{even} }, $n;
}
}
say join ', ', #{ $nums{even} };
See perllol for more on this.
As for values, multiple elements can have the same value in both hashes and arrays.
$counts{a} = 3;
$counts{b} = 3;
$counts[5] = 3;
$counts[6] = 3;
Assuming talking about a "%hash"
Then:
Duplicate keys not allowed.
Duplicate values allowed.
This is easy to reason about because it is a mapping of a particular Key to a particular Value where the Value plays no part in the look-up and is thus independent upon other Values.
Please try and run this code, it executes without errors.
I hope this is what you were asking!
#!/usr/bin/perl
use strict;
use warnings;
my %hash = ('a' => 1, 'a' => 2, 'b' => 4 );
print values %hash, "\n\n";
print keys %hash, "\n\n";
You can try to use Hash::MultiKey module from CPAN.
(I used Data::Dumper to show how hash is exactly looks - it is not necessary here)
use Data::Dumper;
use Hash::MultiKey;
tie my %multi_hash, 'Hash::MultiKey';
$multi_hash{['foo', 'foo', 'baz']} = "some_data";
for (keys %multi_hash) {
print #$_,"\n";
};
print Dumper\%multi_hash;
And the output shoud be () :
foofoobaz
$VAR1 = {
'ARRAY(0x98b6978)' => 'some_data'
};
So technically speaking Hash::MultiKey let you create reference as a hash key.
Yes a hash can have duplicate keys as I demonstrate below...
Key example: BirthDate|LastNameFirst4Chars|FirstNameInitial|IncNbr
"1959-12-19|Will|K|1" ... "1959-12-19|Will|K|74".
Note: This might be a useful Key for record look ups if someone did not remember their Social Security Nbr
#-- CODE SNIPPET:
#Offsets=(); #-- we will build an array of Flat File record "byte offsets" to random access
#-- for all records matching this ALT KEY with DUPS
for ($i=1; $i<=99; $i++) {
$KEY=$BirthDate . "|" . $LastNameFirst4Chars . "|" . $FirstNameInitial . "|" . $i;
if (exists $Hash{$KEY}) {
push #Offsets, $Hash{$KEY}; #-- add another hash VALUE to the end of the array
}
}