Here's the structure that I'm trying to access
Dumper $resourceAudit
$VAR1 = '{
\'rh6\' => {
\'h\' => 1,
\'n\' => 1
},
\'win2k8\' => {
\'h\' => 1,
\'n\' => 1
},
\'win2k12\' => {
\'h\' => 3,
\'n\' => 3
},
\'win2k3\' => {
\'h\' => 0,
\'n\' => 1
},
\'usim\' => {
\'h\' => 4,
\'n\' => 4
}
}';
So, I know that $resourceAudit is actually a string and so, %$resourceAudit is sure to give me the Can't use string as a HASH reference error.
Is there any way I can get around this and access the 'rh6' key?
$resourceAudit doesn't contain a reference to a hash; it contains a string. That string is Perl code that would return a reference to a hash when executed. You can use eval EXPR to run Perl code.
my $data = eval($serialized_data)
or die("Error executing audit code: $#");
... %$data ...
Related
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
I've tried many different versions of using push and splice, but can't seem to combine two hashes as needed. Trying to insert the second hash into the first inside the 'Item' array:
(
ItemData => { Item => { ItemNum => 2, PriceList => "25.00", UOM => " " } },
)
(
Alternate => {
Description => "OIL FILTER",
InFile => "Y",
MfgCode => "FRA",
QtyAvailable => 29,
Stocked => "Y",
},
)
And I need to insert the second 'Alternate' hash into the 'Item' array of the first hash for this result:
(
ItemData => {
Item => {
Alternate => {
Description => "OIL FILTER",
InFile => "Y",
MfgCode => "FRA",
QtyAvailable => 29,
Stocked => "Y",
},
ItemNum => 2,
PriceList => "25.00",
UOM => " ",
},
},
)
Can someone suggest how I can accomplish this?
Assuming you have two hash references, this is straight-forward.
my $item = {
'ItemData' => {
'Item' => {
'PriceList' => '25.00',
'UOM' => ' ',
'ItemNum' => '2'
}
}
};
my $alt = {
'Alternate' => {
'MfgCode' => 'FRA',
'Description' => 'OIL FILTER',
'Stocked' => 'Y',
'InFile' => 'Y',
'QtyAvailable' => '29'
}
};
$item->{ItemData}->{Item}->{Alternate} = $alt->{Alternate};
The trick here is not to actually merge $alt into some part of $item, but to only take the specific part you want and put it where you want it. We take the Alternate key from $alt and put it's content into a new Alternate key inside the guts of $item.
Adam Millerchip pointed out in a hence deleted comment that this is not a copy. If you alter any of the keys inside of $alt->{Alternative} after sticking it into $item, the data will be changed inside of $item as well because we are dealing with references.
$item->{ItemData}->{Item}->{Alternate} = $alt->{Alternate};
$alt->{Alternate}->{InFile} = 'foobar';
This will actually also change the value of $item->{ItemData}->{Item}->{Alternate}->{InFile} to foobar as seen below.
$VAR1 = {
'ItemData' => {
'Item' => {
'ItemNum' => '2',
'Alternate' => {
'Stocked' => 'Y',
'MfgCode' => 'FRA',
'InFile' => 'foobar',
'Description' => 'OIL FILTER',
'QtyAvailable' => '29'
},
'UOM' => ' ',
'PriceList' => '25.00'
}
}
};
References are supposed to do that, because they only reference something. That's what's good about them.
To make a real copy, you need to dereference and create a new anonymous hash reference.
# create a new ref
# deref
$item->{ItemData}->{Item}->{Alternate} = { %{ $alt->{Alternate} } };
This will create a shallow copy. The values directly inside of the Alternate key will be copies, but if they contain references, those will not be copied, but referenced.
If you do want to merge larger data structures where more than the content of one key needs to be merged, take a look at Hash::Merge instead.
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 } },
}
I dumped out a Perl variable called $prefs and got this:
$VAR1 = bless( {
'USERID' => 1286,
'PREFS' => {
'1' => {
'VALUE' => 1,
'OTHERS_POST' => 1,
'CLIENTS_POST' => 1,
'ASSIGNED_TASKS' => 1
}
},
'dbh' => bless( {
'_sth' => bless( {}, 'DBI::st' ),
'_dbh' => bless( {}, 'DBI::db' )
}, 'Taskman::DB' )
}, 'USystems::UserPrefs' );
I'm pretty new to Perl, and I was wondering if someone can break down on whether or not it is possible to access specific data within this variable.
Like if I wanted to do an if statement such as
if (OTHERS_POST == 1) {
// code }
How would I get to the actual OTHERS_POST inside the $prefs variable
$prefs->{PREFS}->{1}->{OTHERS_POST};
I have a hash like this:
{ ABC => [1, 2],
1 => [11, 12,13,14],
13 => [17,20] }
I want to generate a hash of hashes like this:
(ABC => { 1 => {11 => {},
12 => {},
13 => { 17 => {}
20 = {} },
14 => {}
},
2 => {}
}
)
The above hash is nothing but a tree with a root node and further child nodes.
I understand we have to use recursion to check child nodes for every parent node. I have looked at the question previously asked here. I am unable to understand how during recursion specific node's data is stored under its particular parent key. In other words how can hash of hashes be populated recursively ?
Appreciate any pointers or explanation.
Thanks for your time
The real problem is that you don't really know what you want.
{ABC => 1 => 11 => {}
=> 1 => 12 => {}
=> 1 => 13 => 17 => {}
=> 20 = {}
=> 1 => 14 => {}
}
is just a really weird way of writing
{
ABC => "1",
11 => {},
1 => "12",
{} => "1",
13 => "17",
{} => "20",
{} => "1",
14 => {},
}
That makes no sense. I think you actually want
{
ABC => {
1 => {
11 => {},
12 => {},
13 => {
17 => {},
20 => {},
},
14 => {},
},
},
}
Now that you know what you want, you should take a stab at implementing it.
You can use the code I defined here: How can I merge several hashes into one hash in Perl?
having defined #hash_list so:
my #hash_list
= { map { ref() eq 'ARRAY' ? { map {; $_ => {} } #$_ } : $_ }
%{{ ABC => [1, 2]
, 1 => [11, 12,13,14]
, 13 => [17,20]
}}
};
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my %data = (
ABC => [1, 2],
1 => [11, 12, 13, 14],
13 => [17, 20]
);
my %hash;
sub modify_hash {
my ($base, $ref) = #_;
for my $k (keys %$ref) {
if (exists $base->{$k}) {
$ref->{$k} = $base->{$k};
delete $base->{$k};
}
modify_hash($base, $ref->{$k});
}
}
map { %{$hash{$_}} = map { $_ => {}; } #{$data{$_}}; } keys %data;
map { modify_hash(\%hash, $hash{$_}); } keys %hash;
print Dumper(\%hash);
output:
$VAR1 = {
'ABC' => {
'1' => {
'11' => {},
'13' => {
'17' => {},
'20' => {}
},
'12' => {},
'14' => {}
},
'2' => {}
}
};