Print the value in Hash of Hash for two different keys in Perl - perl

Below is a hash in Perl:
my %hash = (
'episode1' => {
'when' => '08.13.97',
'airdate' => '08.13.97',
'episodenumber' => '101',
'id' => '103511',
'title' => 'Cartman Gets an Anal Probe',
'available' => 'true'
},
'episode2' => {
'when' => '08.20.97',
'airdate' => '08.20.97',
'episodenumber' => '102',
'id' => '1035156',
'title' => 'Weight Gain 4000',
'available' => 'true'
}
);
I want to print the "id" of both episodes,but the below code is not working:
foreach my $key1 ( keys %hash ) {
foreach my $key2 ( keys %{$hash{$key1}} ) {
print "$hash{$key1}{$key2}{id}\n";
}
}
Please help.

The problem is that you're trying to print something that doesn't exist: There is no value that matches $hash{$key1}{$key2}{id}.
Try this code, which prints out the value in the hash of hashes that has the key "id":
use strict;
use warnings;
for my $episode (keys %hash){
print "$hash{$episode}{id}\n";
}
103511
1035156

Try using map:
my #ids = map { $hash{$_}{"id"} } sort keys %hash;
Or if you still need the results as a hash:
my %ids_by_key = map { ($_, $hash{$_}{"id"}) } keys %hash;

Try this:
foreach my $key1 ( keys %hash ) {
print "$hash{$key1}{id}\n";
}
or
foreach my $key1 ( keys %hash ) {
foreach my $key2 ( keys %{$hash{$key1}} ) {
print "$hash{$key1}{$key2}\n" if($key2 eq 'id');
}
}

Related

I can't print keys and values in Perl

I have a data structure that I got from this code.
my $name = $data->{Instances}->[0]->{Tags};
That data structure looks like this
$VAR1 = [
{
'Key' => 'Name',
'Value' => 'fl-demo'
},
{
'Value' => 'FL',
'Key' => 'state'
}
];
I'm trying to print the keys and values with this
foreach my $key (sort keys %$name) {
my $value = $name->{$key};
print "$key => $value\n";
}
I'm getting
Not a HASH reference at ./x.pl line 19.
The tags are returned as an array, not a hash. So you're looking at doing something like this, instead, to iterate over them:
foreach my $tag (#$name) {
my $key = $tag->{Key};
my $val = $tag->{Value};
print "$key => $val\n";
}
The data structure dump of variable $name indicates that you have array reference.
You can use loop to output the data of interest, do not forget to dereference $name variable.
use strict;
use warnings;
use feature 'say';
my $name = [
{
'Key' => 'Name',
'Value' => 'fl-demo'
},
{
'Value' => 'FL',
'Key' => 'state'
}
];
say "$_->{Key} = $_->{Value}" for #$name;
Output
Name = fl-demo
state = FL
Elaborating on a previous answer:
$name is a reference to an array containing references to hashes.
#$name and #{$name} (equivalent representations) refer to the array that $name references.
${$name}[0] and $name->[0] (equivalent representations) refer to the first hash in the array referenced by $name.
${$name}[0]{'Key'}, $name->[0]->{'Key'}, etc. (equivalent representations) refer to 'Key''s hash value in the first hash in the array referenced by $name.
As such, the following would iterate over all array and hash elements:
foreach my $hashref ( #{$name} )
{
foreach my $key ( sort(keys(%{$hashref})) )
{
printf("%s => %s\n",$key,$hashref->{$key});
}
print "\n";
}
Or, more compactly (and arguably unreadably):
printf("%s\n",join("\n", map {
my $h = $_;
join(', ', map { sprintf('%s=%s',$_,$h->{$_}) } sort(keys(%{$h})) );
} #{$name} ));

Perl get value of a key

I have been stuck in trying to create an array of keys (example_com,example_ca ..etc) if they are set to 1, I have tried using for loop and foreach loop, but keep getting ARRAY# error.
$VAR1 = [
{
'example_com' => '1',
'example_ca' => '1'
}
];
Thanks
This will be because you have an array containing a hash. The array is one element long.
So you 'get' to the hash, by dereferencing element zero.
Thus:
my $hash_ref = $VAR1->[0];
print join "\n", keys %{$hash_ref},"\n";
foreach my $key ( keys %{$VAR1->[0]} ) {
print "$key => $VAR1->[0]{$key}\n";
}
Exactly for you source data:
my #array_of_keys = ();
for( keys %{ $VAR1->[0] } ) {
push #array_of_keys, $_ if $VAR1->[0]{ $_ } eq '1';
}
print "Keys with 1: #array_of_keys";
An expanded example of how to get an array of keys if you have multiple hashes in your container array:
my $VAR1 = [
{
'example_com' => '1',
'example_ca' => '1',
'not_set' => '0'
},
{
'EXAMPLE_com' => '1',
'EXAMPLE_ca' => '1',
'NOT_SET' => '0',
}
];
my #arrayOfHashes = #{$VAR1};
foreach my $array (#arrayOfHashes)
{
my #onlyOnes;
my #arrayOfKeys = sort keys %{$array};
foreach my $key (#arrayOfKeys)
{
next if ($array->{$key} ne 1);
push #onlyOnes, $key;
}
print "\nKey names:\n";
foreach my $key (#onlyOnes)
{
print "$key\n";
}
}
output:
Key names:
example_ca
example_com
Key names:
EXAMPLE_ca
EXAMPLE_com

perl hash of hashes - for eacy key group inner keys and respective values

I have a hash of hashes retrieved from json and I am trying to flatten hash, group and print values. That is, for each key1 group all keys and respective values.Trying to achieve this in perl using below code but, it is printing individual values each ID and names but not grouped values. Could you please help me how to group and print keys and values.
Data:
VAR1= {
'ID1' => {'Name1' => {'Address1' => {'Mod1' => ['pol1']}},
Name2' => {'Address1' => {'Mod2' => ['pol2']}}},
'ID2' => {'Name3' => {'Address3' => {'Mod3' => ['pol3']}}},
'ID3' => {'Name4' => {'Address4' => {'Mod4' => ['pol1, pol2, pol3']}}}
}
Expected Output:
ID1 => [Name1,Address1,mod1(pol1), Name2,Address1,mod2(pol2)],
ID2 => [Name3,Address3,mod3(pol3)],
ID3 => [Name4,Address4,mod4(pol1,pol2,pol3)]
Code1:
for my $id (#id_list) {
foreach my $item (sort (slurp(\%{$new{$id}}))) {
my $data = join(',', #$item);
print "data.. $data\n";
push(#results,$data);
}
}
print map {"$_"} #results;
Code2:
foreach my $id (sort keys %new){
foreach my $name (keys %{$new{$id} }) {
foreach my $address (keys %{$new{$id}{$name}}) {
foreach my $mod (keys %{$new{$id}{$name}{$address}) {
foreach my $value (#{$new{$id}{$name}{$address}{$mod}}) {
my $sp = ":";
my $reasons = join(',',$id,$name,$address,$mod,$value.$sp);
push (#rea, $reasons);
}
}
}
}
}
With such a nested structure you want to go systematically, like in your "Code2".
use warnings;
use strict;
use feature qw(say);
my %id_list = (
ID1 => {
Name1 => {'Address1' => {'Mod1' => ['pol1']}},
Name2 => {'Address1' => {'Mod2' => ['pol2']}}
},
ID2 => { 'Name3' => {'Address3' => {'Mod3' => ['pol3']}} },
ID3 => { 'Name4' => {'Address4' => {'Mod4' => ['pol1, pol2, pol3']}} }
);
my (%flattened, #ar, $modvals);
foreach my $id (sort keys %id_list){
foreach my $name (keys %{$id_list{$id} }) {
foreach my $address (keys %{$id_list{$id}{$name}}) {
foreach my $mod (keys %{$id_list{$id}{$name}{$address}}) {
$modvals = "$mod(" .
join(',', #{$id_list{$id}{$name}{$address}{$mod}})
. ')';
$modvals =~ s/,$//; # strip the last comma
push #ar, $name, $address, $modvals;
}
}
}
$flattened{$id} = [ #ar ];
#ar = ();
}
say "$_ => #{$h{$_}}" for sort keys %flattened;
Output
ID1 => Name1 Address1 Mod1(pol1) Name2 Address1 Mod2(pol2)
ID2 => Name3 Address3 Mod3(pol3)
ID3 => Name4 Address4 Mod4(pol1, pol2, pol3)
This generates the string for mod3 as (pol1, ...) -- as given in input, as a single string. If that is chaged to a list of pol in input, the spaces between pol's will be gone in output.
A hash with id keys and array ref with respective entries is generated.

How can I find which keys in a Perl multi-level hash correspond to a given value?

I have a data structure which looks like this:
my %hoh = (
'T431567' => {
machin => '01',
bidule => '02',
truc => '03',
},
'T123456' => {
machin => '97',
bidule => '99',
truc => '69',
},
'T444444' => {
machin => '12',
bidule => '64',
truc => '78',
},
);
I want to search the various values of truc for a particular value and find the top-level attribute which corresponds to that entry. For example, looking for a value of 78, I want to find the result 'T444444', because $hoh{T444444}{truc} is 78.
How can I do this, please?
You can do this with grep:
my #keys = grep { $hoh{$_}{truc} == 78 } keys %hoh;
Note that this can return more than one key, if there are duplicate values in the hash. Also note that this is not particularly efficient, since it has to search the entire hash. In most cases it's probably fine, but if the hash can be very large and you may need to run lots of such queries against it, it may be more efficient to build a reverse index as suggested by Sobrique:
my %trucs;
foreach my $part (keys %hoh) {
my $val = $hoh{$part}{truc};
push #{ $trucs{$val} }, $part;
}
my #keys = #{ $trucs{78} };
or, more generally:
my %index;
foreach my $part (keys %hoh) {
my %data = %{ $hoh{$part} };
foreach my $key (keys %data) {
my $val = $data{$key};
push #{ $index{$key}{$val} }, $part;
}
}
my #keys = #{ $index{truc}{78} };
Can't with that data structure as is - There is no 'backwards' relationship from value to key without you creating it.
You've two options - run a search, or create an 'index'. Practically speaking, these are the same, just one saves the results.
my %index;
foreach my $key ( keys %hoh ) {
my $truc = $hoh{$key}{'truc'};
$index{$truc} = $key;
}
Note - won't do anything clever if the 'truc' numbers are duplicated - it'll overwrite. (Handling this is left as an exercise to the reader).
This solution is similar to those already posted, but it uses the each operator to process the original hash in fewer lines of code, and probably more quickly.
I have added the dump output only so that you can see the form of the structure that is built.
use strict;
use warnings;
my %hoh = (
T123456 => { bidule => '99', machin => '97', truc => '69' },
T431567 => { bidule => '02', machin => '01', truc => '03' },
T444444 => { bidule => '64', machin => '12', truc => '78' },
);
my %trucs;
while ( my ($key, $val) = each %hoh ) {
next unless defined( my $truc = $val->{truc} );
push #{ $trucs{$truc} }, $key ;
}
use Data::Dump;
dd \%trucs;
print "\n";
print "$_\n" for #{ $trucs{78} };
output
{ "03" => ["T431567"], "69" => ["T123456"], "78" => ["T444444"] }
T444444
If you can guarantee that the answer is unique, i.e. that there is never more than one element of the original hash that has a given value for the truc entry, or you are interested only in the last one found, then you can write this still more neatly
my %trucs;
while ( my ($key, $val) = each %hoh ) {
next unless defined( my $truc = $val->{truc} );
$trucs{$truc} = $key ;
}
print $trucs{78}, "\n";
output
T444444
Simplest of all, if there is always a truc entry in each second-level hash, and its values is guaranteed to be unique, then this will do the job
my %trucs = map { $hoh{$_}{truc} => $_ } keys %hoh;
print $trucs{78}, "\n";
with the output as above.

How do I sort hash of hashes by value using perl?

I have this code
use strict;
use warnings;
my %hash;
$hash{'1'}= {'Make' => 'Toyota','Color' => 'Red',};
$hash{'2'}= {'Make' => 'Ford','Color' => 'Blue',};
$hash{'3'}= {'Make' => 'Honda','Color' => 'Yellow',};
foreach my $key (keys %hash){
my $a = $hash{$key}{'Make'};
my $b = $hash{$key}{'Color'};
print "$a $b\n";
}
And this out put:
Toyota Red Honda Yellow Ford Blue
Need help sorting it by Make.
#!/usr/bin/perl
use strict;
use warnings;
my %hash = (
1 => { Make => 'Toyota', Color => 'Red', },
2 => { Make => 'Ford', Color => 'Blue', },
3 => { Make => 'Honda', Color => 'Yellow', },
);
# if you still need the keys...
foreach my $key ( #
sort { $hash{$a}->{Make} cmp $hash{$b}->{Make} } #
keys %hash
)
{
my $value = $hash{$key};
printf( "%s %s\n", $value->{Make}, $value->{Color} );
}
# if you don't...
foreach my $value ( #
sort { $a->{Make} cmp $b->{Make} } #
values %hash
)
{
printf( "%s %s\n", $value->{Make}, $value->{Color} );
}
print "$_->{Make} $_->{Color}" for
sort {
$b->{Make} cmp $a->{Make}
} values %hash;
plusplus is right... an array of hashrefs is likely a better choice of data structure. It's more scalable too; add more cars with push:
my #cars = (
{ make => 'Toyota', Color => 'Red' },
{ make => 'Ford' , Color => 'Blue' },
{ make => 'Honda' , Color => 'Yellow' },
);
foreach my $car ( sort { $a->{make} cmp $b->{make} } #cars ) {
foreach my $attribute ( keys %{ $car } ) {
print $attribute, ' : ', $car->{$attribute}, "\n";
}
}