create an array from a part of a hash in perl - perl

I have a hash which contain sub hash, I want to abstract that sub hash separately and create a array from that,
hash look like
'a1' => '1',
'a2' => '2'.
'Def' => [
'd' => 'x',
'e' => 'y'
]
I need to make a separate hash for 'Def'. and print only 'Def' as a array

Its hard from reading your question to know just exactly what you are trying to achieve but my interpretation of it is that you want to extract the anonymous hash allocated to def and store it in another hash. Then you want to print this hash as an array. I have also included examples to print just the keys of the values of the hash.
use strict;
use Data::Dumper;
my %first_hash = (
a1 => '1',
a2 => '2',
def => {
d => 'x',
e => 'y'
}
);
my %second_hash = %{$first_hash{'def'}};
my #full_array = %second_hash;
my #keys_array = keys %second_hash;
my #values_array = values %second_hash;
print Dumper (\%first_hash);
print Dumper (\%second_hash);
print "full array: ", join(' ',#full_array), "\n";
print "keys array: ", join(' ',#keys_array), "\n";
print "values array: ", join(' ',#values_array), "\n";
OUTPUT
$VAR1 = {
'a2' => '2',
'def' => {
'e' => 'y',
'd' => 'x'
},
'a1' => '1'
};
$VAR1 = {
'e' => 'y',
'd' => 'x'
};
full array: e y d x
keys array: e d
values array: y x

Below you'll find the answer.
print "#{$a{'Def'}}";

Related

Perl eval Data::Dumper inconsistency

I have to serialize and deserialize in Perl. I am aware that Data::Dumper and eval are not the best suited ones for this job but I am not allowed to modify this aspect in the legacy scripts which I am working on.
Below are two ways ( CODE 1 and CODE 2 ) to use eval.
In CODE 1, the hash is available as a string before being deserialized via eval.
In CODE 2, the hash is serialized using Dumper before being deserialized via eval.
In both the code samples, one of two attempted ways to deserialize works. Why does the other way to deserialize not work ?
CODE 1
my $r2 = "(
'w' => {
'k2' => 5,
'k1' => 'key',
'k3' => [
'a',
'b',
2,
'3'
]
},
'q' => 2
)";
my %z;
eval "\%z = $r2"; ####### Works.
print "\%z = [".Data::Dumper::Dumper (\%z)."] ";
my $answer = eval "$r2"; #### Does NOT work.
print "\n\nEvaled = [".Dumper($answer)."] ";
Output
%z = [$VAR1 = {
'w' => {
'k2' => 5,
'k1' => 'key',
'k3' => [
'a',
'b',
2,
'3'
]
},
'q' => 2
};
]
Evaled = [$VAR1 = 2;
]
But below code works in reverse manner :
CODE 2
my %a = ( "q" =>2, "w"=>{ "k1"=>"key", "k2"=>5, k3=>["a", "b", 2, "3",], }, ); **# Same hash as above example.**
$Data::Dumper::Terse=1;
$Data::Dumper::Purity = 1;
my $r2 = Dumper(\%a);
my %z;
eval '\%z = $r2';
print "\n\n\%z = [".Dumper(\%z)."] "; #### Does NOT work.
my $answer = eval $r2;
print "\n\nEvaled = [".Dumper($answer)."] "; ####### Works.
Output
%z = [$VAR1 = {};
]
Evaled = [$VAR1 = {
'w' => {
'k2' => 5,
'k1' => 'key',
'k3' => [
'a',
'b',
2,
'3'
]
},
'q' => 2
};
]
First of all, please don't put comments that result in syntax errors (**).
Notice that the string you provided in the first code block produces different data structure than the Dumper function. In the first block you are creating a hash, but you don't assign it to any variable. In case of the Dumper function, anonymous hash is created and it's reference is passed to the $VAR variable.
To make the first code work, you should replace ( with { to create anonymous hash and then assign it to a variable, for example:
my $r2 = "$VAR = {
'w' => {
'k2' => 5,
'k1' => 'key',
'k3' => [
'a',
'b',
2,
'3'
]
},
'q' => 2
}";
Don't use Data::Dumper to serialize data. Having said that...
This is a problem of scalar and list context. In the second eval, you have:
my $answer = ...;
Since you are assigning to a scalar, the right side is evaluated in scalar context. That means the eval is in scalar context. That value is:
(
'w' => {
'k2' => 5,
'k1' => 'key',
'k3' => [
'a',
'b',
2,
'3'
]
},
'q' => 2
)
That looks like a list, but it's really the comma operator in scalar context. That evaluates the left hand side, discards that result, and returns the righthand side. So, my $x = ( 'left', 'right' ) assigns right to $x. This is covered in What is the difference between a list and an array? in perlfaq4.
In your question, you see that $r gets the value 2. That's the rightmost value in the comma chain, so that's the value you get back in scalar context. Change that to another value (perhaps 'duck'), and that's the value that you'll get back:
my $r2 = "(
'w' => {
'k2' => 5,
},
'q' => 'duck'
)";
my $answer = eval "$r2";
use Data::Dumper;
print "Evaled =\n" . Dumper($answer);
It's not a number, which confuses people because they think it's some sort of count:
Evaled =
$VAR1 = 'duck';
Change that to assign in list context (that hash assignment is a list assignment) and you get the right answer:
my $r2 = "(
'w' => {
'k2' => 5,
},
'q' => 'duck'
)";
my #answer = eval "$r2";
use Data::Dumper;
print "Evaled =\n" . Dumper(\#answer);
Now it's the data structure you thought it should be:
Evaled =
$VAR1 = [
'w',
{
'k2' => 5
},
'q',
'duck'
];

Perl hashref printing keys

I have the following hashref :-
my $hashref = {'a'=>(1,2,3,4),
'b'=>(5,6,7,8)};
then I use the following to just print the keys (i.e. 'a' and 'b') :-
foreach (keys %$hashref){
print "\n".$_."\n";
}
This prints the following output:-
4
a
7
2
5
Trying to print the datastructure hashref using Data::Dumper gives the following output:-
$VAR1 = {
'4' => 'b',
'a' => 1,
'7' => 8,
'2' => 3,
'5' => 6
};
My question is :-
1) How to just print the correct keys i.e. 'a' and 'b'.
2) Why does the data structure look like the one in the above output and not like:-
$VAR1 = {
'a' => (1,2,3,4),
'b' => (5,6,7,8)
};
You are defining the hash wrong. It interprets this:
'a'=>(1,2,3,4),
'b'=>(5,6,7,8)
as simply a list of 10 elements. (Remember that a hash can also be declared using a simple list, the => operator is optional.) Instead, use square brackets to make your values into arrayref literals:
'a'=>[1,2,3,4],
'b'=>[5,6,7,8]
Which Data::Dumper should call:
$VAR1 = {
'a' => [1,2,3,4],
'b' => [5,6,7,8]
};

How to create hash of hash key

I have the following code
use strict;
use warnings;
use Data::Dumper;
my $s = "12 A P1
23 B P5
24 C P2
15 D P1
06 E P5 ";
my $hash = {};
my #a = split(/\n/, $s);
foreach (#a)
{
my $c = (split)[2];
my $d = (split)[1];
my $e = (split)[0];
push(#{$hash->{$c}}, $d);
}
print Dumper($hash );
I am getting the output
$VAR1 = {
'P5' => [
'B',
'E'
],
'P2' => [
'C'
],
'P1' => [
'A',
'D'
]
};
But I want the output like
$VAR1 = {
'P5' => {
'E' => '06',
'B' => '23'
},
'P2' => {
'C' => '24'
},
'P1' => {
'A' => '12',
'D' => '15'
}
};
Please help.
You need to use a hash if you want a hash as output.
No need to split three times and use postscripts, just split once and assign all variables. Also no need to initialize a scalar as an empty hash, perl will take care of that for you.
I renamed the variables for increased readability.
my $string = "12 A P1
23 B P5
24 C P2
15 D P1
06 E P5 ";
my $hash;
my #lines = split(/\n/, $string);
foreach (#lines)
{
my ($value, $key2, $key) = split;
$hash->{$key}{$key2} = $value;
}
print Dumper($hash );
Be aware that if you have multiple values with the same keys, they will overwrite each other. In that case, you'd need to push the values onto an array instead:
push #{$hash->{$key}{$key2}}, $value;
Well it's not that different from what you have. Just replace the push with a hash-assign (thank you auto-vivification):
foreach (#a)
{
my ($e, $d, $c) = split;
$hash->{$c}->{$d} = $e;
}
Additionally I have re-arranged the "split" so that it's just called once per line.

How do I loop through a hash?

Given the following variable:
$test = {
'1' => 'A',
'2' => 'B',
'3' => 'C',
'4' => 'G',
'5' => 'K',
}
How can loop through all assignments without knowing which keys I have?
I would like to fill a select box with the results as label and the keys as hidden values.
Just do a foreach loop on the keys:
#!/usr/bin/perl
use strict;
use warnings;
my $test = {
'1' => 'A',
'2' => 'B',
'3' => 'C',
'4' => 'G',
'5' => 'K',
};
foreach my $key(keys %$test) {
print "key=$key : value=$test->{$key}\n";
}
output:
key=4 : value=G
key=1 : value=A
key=3 : value=C
key=2 : value=B
key=5 : value=K
You can use the built-in function each:
while (my ($key, $value) = each %$test) {
print "key: $key, value: $value\n";
}
You can find out what keys you have with keys
my #keys = keys %$test; # Note that you need to dereference the hash here
Or you could just do the whole thing in one pass:
print map { "<option value='$_'>$test->{$_}</option>" } keys %$test;
But you'd probably want some kind of order:
print map { "<option value='$_'>$test->{$_}</option>" } sort keys %$test;
… and you'd almost certainly be better off moving the HTML generation out to a separate template system.

Perl, convert numerically-keyed hash to array

If I have a hash in Perl that contains complete and sequential integer mappings (ie, all keys from from 0 to n are mapped to something, no keys outside of this), is there a means of converting this to an Array?
I know I could iterate over the key/value pairs and place them into a new array, but something tells me there should be a built-in means of doing this.
You can extract all the values from a hash with the values function:
my #vals = values %hash;
If you want them in a particular order, then you can put the keys in the desired order and then take a hash slice from that:
my #sorted_vals = #hash{sort { $a <=> $b } keys %hash};
If your original data source is a hash:
# first find the max key value, if you don't already know it:
use List::Util 'max';
my $maxkey = max keys %hash;
# get all the values, in order
my #array = #hash{0 .. $maxkey};
Or if your original data source is a hashref:
my $maxkey = max keys %$hashref;
my #array = #{$hashref}{0 .. $maxkey};
This is easy to test using this example:
my %hash;
#hash{0 .. 9} = ('a' .. 'j');
# insert code from above, and then print the result...
use Data::Dumper;
print Dumper(\%hash);
print Dumper(\#array);
$VAR1 = {
'6' => 'g',
'3' => 'd',
'7' => 'h',
'9' => 'j',
'2' => 'c',
'8' => 'i',
'1' => 'b',
'4' => 'e',
'0' => 'a',
'5' => 'f'
};
$VAR1 = [
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j'
];
OK, this is not very "built in" but works. It's also IMHO preferrable to any solution involving "sort" as it's faster.
map { $array[$_] = $hash{$_} } keys %hash; # Or use foreach instead of map
Otherwise, less efficient:
my #array = map { $hash{$_} } sort { $a<=>$b } keys %hash;
Perl does not provide a built-in to solve your problem.
If you know that the keys cover a particular range 0..N, you can leverage that fact:
my $n = keys(%hash) - 1;
my #keys_and_values = map { $_ => $hash{$_} } 0 .. $n;
my #just_values = #hash{0 .. $n};
This will leave keys not defined in %hashed_keys as undef:
# if we're being nitpicky about when and how much memory
# is allocated for the array (for run-time optimization):
my #keys_arr = (undef) x scalar %hashed_keys;
#keys_arr[(keys %hashed_keys)] =
#hashed_keys{(keys %hashed_keys)};
And, if you're using references:
#{$keys_arr}[(keys %{$hashed_keys})] =
#{$hashed_keys}{(keys %{$hashed_keys})};
Or, more dangerously, as it assumes what you said is true (it may not always be true … Just sayin'!):
#keys_arr = #hashed_keys{(sort {$a <=> $b} keys %hashed_keys)};
But this is sort of beside the point. If they were integer-indexed to begin with, why are they in a hash now?
As DVK said, there is no built in way, but this will do the trick:
my #array = map {$hash{$_}} sort {$a <=> $b} keys %hash;
or this:
my #array;
keys %hash;
while (my ($k, $v) = each %hash) {
$array[$k] = $v
}
benchmark to see which is faster, my guess would be the second.
#a = #h{sort { $a <=> $b } keys %h};
Combining FM's and Ether's answers allows one to avoid defining an otherwise unnecessary scalar:
my #array = #hash{ 0 .. $#{[ keys %hash ]} };
The neat thing is that unlike with the scalar approach, $# works above even in the unlikely event that the default index of the first element, $[, is non-zero.
Of course, that would mean writing something silly and obfuscated like so:
my #array = #hash{ $[ .. $#{[ keys %hash ]} }; # Not recommended
But then there is always the remote chance that someone needs it somewhere (wince)...
$Hash_value =
{
'54' => 'abc',
'55' => 'def',
'56' => 'test',
};
while (my ($key,$value) = each %{$Hash_value})
{
print "\n $key > $value";
}
We can write a while as below:
$j =0;
while(($a1,$b1)=each(%hash1)){
$arr[$j][0] = $a1;
($arr[$j][1],$arr[$j][2],$arr[$j][3],$arr[$j][4],$arr[$j][5],$arr[$j][6]) = values($b1);
$j++;
}
$a1 contains the key and
$b1 contains the values
In the above example i have Hash of array and the array contains 6 elements.
An easy way is to do #array = %hash
For example,
my %hash = (
"0" => "zero",
"1" => "one",
"2" => "two",
"3" => "three",
"4" => "four",
"5" => "five",
"6" => "six",
"7" => "seven",
"8" => "eight",
"9" => "nine",
"10" => "ten",
);
my #array = %hash;
print "#array"; would produce the following output,
3 three 9 nine 5 five 8 eight 2 two 4 four 1 one 10 ten 7 seven 0 zero
6 six