I've been trying to read the following structure in Perl:
my %Homes = (
"Home 1" => [
'TYPE_OF_HOUSE' => "Villa",
'FIELD1' => ["1","2","3"],
'Field2' => ["2","3","4"],
'Field3' => ["3","4","5"],
],
"Home 2" => [
'TYPE_OF_HOUSE' => "Duplex",
'FIELD1' => ["1","2","3"],
'Field2' => ["2","3","4"],
'Field3' => ["3","4","5"],
],
"Home 3" => [
'TYPE_OF_HOUSE' => "Apartment",
'FIELD1' => ["1","2","3"],
'Field2' => ["2","3","4"],
'Field3' => ["3","4","5"],
],
);
By using the following:
my #data_Array;
my ($inner_Key,$key,$names_ref);
foreach $key (keys %Homes)
{
print("Inner values of house $key are:");
foreach $inner_Key ( #{$Homes{$key}})
{
if ($inner_Key != "TYPE_OF_HOUSE")
{
$names_ref = \#$inner_Key;
#data_Array = #{$names_ref};
print($data_Array[0]);
print($data_Array[1]);
print($data_Array[2]);
}
else
{
print($inner_Key);
}
}
}
I successfully printed the: "Home 1", "Home 2", but when trying to read the inner content, even though I used reference to an array, it didn't go well. What did I miss? My goal was to "identify" the type of field, and if it was TYPE_OF_HOUSE, then simply print it because it is not an array. Otherwise, print the inner content of its array.
You must use strict and warnings because they will tell you a lot about what is wrong with your code.
You need to use ne instead of != when comparing strings.
You should use a different data structure to make it simpler to access inner data elements. You have a hash-of-arrays, but it would make more sense as a hash-of-hashes. By using => for 'TYPE_OF_HOUSE' => "Villa", you've tricked yourself into believing that is a hash. However, it is just a flat array. The 1st element of the array is TYPE_OF_HOUSE, the 2nd element is Villa, the 3rd is FIELD1, the 4th is an array ref, etc.
use warnings;
use strict;
my %Homes = (
"Home 1" => {
'TYPE_OF_HOUSE' => "Villa",
'FIELD1' => [ "1", "2", "3" ],
'Field2' => [ "2", "3", "4" ],
'Field3' => [ "3", "4", "5" ],
},
"Home 2" => {
'TYPE_OF_HOUSE' => "Duplex",
'FIELD1' => [ "1", "2", "3" ],
'Field2' => [ "2", "3", "4" ],
'Field3' => [ "3", "4", "5" ],
},
"Home 3" => {
'TYPE_OF_HOUSE' => "Apartment",
'FIELD1' => [ "1", "2", "3" ],
'Field2' => [ "2", "3", "4" ],
'Field3' => [ "3", "4", "5" ],
},
);
my #data_Array;
my ( $inner_Key, $key, $names_ref );
foreach $key ( keys %Homes ) {
print("Inner values of house $key are:");
print "\n";
foreach $inner_Key ( keys %{ $Homes{$key} } ) {
if ( $inner_Key ne "TYPE_OF_HOUSE" ) {
#data_Array = #{ $Homes{$key}{$inner_Key} };
print( $data_Array[0] );
print "\n";
print( $data_Array[1] );
print "\n";
print( $data_Array[2] );
print "\n";
}
else {
print($inner_Key);
print "\n";
}
}
}
I used perltidy to improve the indentation. I added newlines to make your print output easier to read.
Related
I am trying to push values into hash. I want to add the values under 'par3'.
e.g.
$VAR1 = { 'obj1' => ['par1',
'par2',
'par3' => ['par4','par5','par6',....]]}
I should also be able to add elements into 'par3' in case 'obj1'-'par1'-'par2'-'par3' matches.
So far I have this, but I can't figure out how can I add "the second level" under 'par3':
push #{$a{$obj}},$par1,$par2,$par3
[ ... ] is a reference to an array. Array elements are scalars. So it is not possible to directly have the structure you seem to be requesting (ie. the par3 => [ ... ] pseudocode from your question). See perldsc
It's not obvious what you are trying to do but a couple of possible ideas might be to use a reference to a hash, or to replace the array with a hash:
use Data::Dumper;
$Var2a = {
'obj1' => [
'par1',
'par2',
{ 'par3' => undef, }
],
};
push #{ $Var2a->{obj1}[2]{par3} }, 'par4', 'par5', 'par6';
print Dumper $Var2a;
$Var2b = {
'obj1' => {
'par1' => undef,
'par2' => undef,
'par3' => undef,
},
};
push #{ $Var2b->{obj1}{par3} }, 'par4', 'par5', 'par6';
print Dumper $Var2b;
I think you want to add children to par3. As such, I think you want pars to have children. If so, you need a different data structure.
# Ordered
my $obj1 = [
{ name => 'par1', children => [ ] },
{ name => 'par2', children => [ ] },
{ name => 'par3', children => [
{ name => 'par4', children => [ ] },
{ name => 'par5', children => [ ] },
{ name => 'par6', children => [ ] },
] },
];
or
# Unordered
my $obj1 = {
par1 => { },
par2 => { },
par3 => {
par4 => { },
par5 => { },
par6 => { },
},
};
To append par7 to par3's children, you would use
# Ordered
use List::Util qw( first );
my $par7 = { name => 'par7', children => [ ] };
my $par3 = first { $_->{ name } eq 'par3' } #$obj1
or die( "par3 not found" );
push #{ $par3->{ children } }, $par7;
or
# Unordered
$obj1->{ par3 }{ par7 } = { };
I have a Hash of following structure in perl -
my %testHash = (
KeyL1 => {
KeyLL1 => {
KeyLLL1 => [1,2],
KeyLLL2 => [2,3],
},
KeyLL2 => {
KeyLLL1 => [1,2],
KeyLLL2 => [2,3],
},
KeyLL3 => {
KeyLLL1 => [1,2],
KeyLLL2 => [2,3],
},
},
KeyL2 => {
KeyLL1 => {
KeyLLL1 => [1,2],
KeyLLL2 => [2,3],
},
KeyLL2 => {
KeyLLL1 => [1,2],
KeyLLL2 => [2,3],
},
KeyLL3 => {
KeyLLL1 => [1,2],
KeyLLL2 => [2,3],
},
},
);
Now, when I am trying to access it the following way, I am getting 'undef' as a result
my %tempHash = $testHash{'KeyL1'};
print Data::Dumper::Dumper($tempHash{'KeyLL1'});
print Data::Dumper::Dumper($tempHash{'KeyLL1'}{'KeyLLL1'});
Result --
$VAR1 = undef; $VAR1 = undef;
Please point to me what am I doing wrong. I am pretty new to perl.
The value of $testHash{'KeyL1'} is a hashref, not a hash.
Hashrefs are scalars. my %tempHash = is not expecting a scalar.
You need to dereference it:
my %tempHash = %{$testHash{'KeyL1'}};
Also, you could do it this way if its just about viewing the structures.
Also try:
print Dumper $testHash{KeyL1} ;
print Dumper $testHash{KeyL1}{KeyLL1} ;
print Dumper $testHash{KeyL1}{KeyLL1}{KeyLLL1} ;
Output:
%_Host#User> ./hash.pl
$VAR1 = {
'KeyLL1' => {
'KeyLLL2' => [
2,
3
],
'KeyLLL1' => [
1,
2
]
},
'KeyLL2' => {
'KeyLLL2' => [
2,
3
],
'KeyLLL1' => [
1,
2
]
},
'KeyLL3' => {
'KeyLLL2' => [
2,
3
],
'KeyLLL1' => [
1,
2
]
}
};
$VAR1 = {
'KeyLLL2' => [
2,
3
],
'KeyLLL1' => [
1,
2
]
};
$VAR1 = [
1,
2
];
%_Host#User>
I want to create url from input hash ref. Suppose I give hash ref as
my $input_hash_ref = {
'1' => 'A',
'2' => 'B',
'3' => {
'4' => {
'5' => {
'6' => [
'ice cream','drink'
],
'7' => 'large'
}
'8' => 'perl'
'9' => 'rosy'
},
'10'=>'june'
},
};
Then this is to be converted as
1=A&2=B&3.4.5.6=ice cream|drinks&3.4.5.7=large&3.8=perl&3.9=rosy&10=june
Help needed.
I just have to say, you shouldn't be composing query strings with a naive implementation with join.
use URI;
use URI::QueryParam;
my $u = URI->new("","http");
Then you can simply:
$u->query_param_append("1" => "A", "2" => "B", ....);
Or even
$u->query_form_hash( %somedata );
Note that this does not automatically deal with your custom schema for serialized nesting, but it does guarantee that you'll emit a valid query string that any server will understand.
Though you can also use a Perl Module to convert from a deeply-nested Hash to a Flat hash and back again:
Data::SplitSerializer
And you can use this to convert between formats on both sides.
Example usage:
use strict;
use warnings;
use utf8;
use Data::SplitSerializer;
use Data::Dump qw(pp);
use URI;
use URI::QueryParam;
my $input_hash = {
'1' => 'A',
'2' => 'B',
'3' => {
'4' => {
'5' => {
'6' => [
'ice cream','drink'
],
'7' => 'large'
}
},
},
'8' => 'june',
'9' => "Challenging & Value",
};
my $flattened = Data::SplitSerializer->new( path_style => 'DZIL' )->serialize($input_hash);
pp $flattened;
my $uri = URI->new("http://example.com/thing?");
$uri->query_form_hash( $flattened );
printf "%s\n", $uri;
my $copy = URI->new( $uri . "" ); # simulate getting it server side
my $copy_hash = $copy->query_form_hash;
pp $copy_hash;
my $deep = Data::
SplitSerializer->new( path_style => 'DZIL' )->deserialize($copy_hash);
pp $deep;
Example Output:
{
"1" => "A",
"2" => "B",
"3.4.5.6[0]" => "ice cream",
"3.4.5.6[1]" => "drink",
"3.4.5.7" => "large",
"8" => "june",
"9" => "Challenging & Value",
}
http://example.com/thing?9=Challenging+%26+Value&3.4.5.6%5B1%5D=drink&2=B&8=june&3.4.5.6%5B0%5D=ice+cream&1=A&3.4.5.7=large
{
"1" => "A",
"2" => "B",
"3.4.5.6[0]" => "ice cream",
"3.4.5.6[1]" => "drink",
"3.4.5.7" => "large",
"8" => "june",
"9" => "Challenging & Value",
}
{
1 => "A",
2 => "B",
3 => { 4 => { 5 => { 6 => ["ice cream", "drink"], 7 => "large" } } },
8 => "june",
9 => "Challenging & Value",
}
use URI::Escape;
sub serial {
my ($h, $p) = #_;
return join "&", map {
my $v = $h->{$_};
my $ref = ref($v);
my $isH = $ref eq "HASH";
my $pp = join ".", grep defined, $p, $_;
$v = $isH ? serial($v,$pp)
: $ref ? join("|", map uri_escape($_), #$v)
: uri_escape($v);
$isH ? $v : "$pp=$v";
}
sort keys %$h;
}
my $input_hash_ref = {
'1' => 'A',
'2' => 'B',
'3' => {
'4' => {
'5' => {
'6' => [
'ice cream','drink'
],
'7' => 'large'
}
},
},
'8' => 'june'
};
print serial($input_hash_ref);
output
1=A&2=B&3.4.5.6=ice cream|drink&3.4.5.7=large&8=june
sub c {
my ($v, $p) = #_;
my $r = ref($v);
return map { c($v->{$_}, $p ? $p . '.' . $_ : $_) } keys(%$v) if $r eq 'HASH';
return $p . '=' . join('|', #$v) if $r eq 'ARRAY';
return $p . '=' . $v;
}
say(join('&', c($input_hash_ref)));
I am trying to parse xml using xml::simple .Here is the output of xml::simple
$VAR1 = {
'soapenv:Body'=>[
{
'ns1:queryResponse'=>[
{
'ns1:result'=>[
{
'ns1:done'=>['true'],
'ns1:queryLocator' => [
{
'xsi:nil' => '1',
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance'
}
],
'ns1:size' => [
'60'
],
'ns1:records' => [
{
'ns2:RefundTransactionTime' => [
'2013-09-12T13:17:18.000-08:00'
],
'xmlns:ns2' => 'http://object.abccom/',
'ns2:MethodType' => [
'CreditCard'
],
'ns2:Gateway' => [
'Chase Paymentech'
],
'ns2:Type' => [
'Electronic'
],
'ns2:RefundDate' => [
'2013-09-12T00:00:00'
],
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
'ns2:Status' => [
'Processed'
],
'ns2:Id' => [
'2c92c0f8410f4d9a014113d2170a2e17'
],
'xsi:type' => 'ns2:Refund',
'ns2:AccountId' => [
'2c92c0f9410f55ee0141132b6c936e15'
],
'ns2:Amount' => [
'99'
],
'ns2:CreatedDate' => [
'2013-09-12T13:17:17.000-08:00'
]
},
{
'xmlns:ns2' => 'http://object.abccom/',
'ns2:MethodType' => [
'CreditCard'
],
'ns2:Type' => [
'External'
],
'ns2:RefundDate' => [
'2013-10-12T00:00:00'
],
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
'ns2:Status' => [
'Processed'
],
'ns2:Id' => [
'2c92c0f8410f4d9a0141145bfbb61a9b'
],
'xsi:type' => 'ns2:Refund',
'ns2:AccountId' => [
'2c92c0f8410f4d8701411411a9ad79b7'
],
'ns2:Amount' => [
'477.74'
],
'ns2:CreatedDate' => [
'2013-09-12T15:47:54.000-08:00'
],
'ns2:Comment' => [
'16 Payment Exception - Chargeback'
]
}
]
}
],
'xmlns:ns1' => 'http://api.abccom/'
}
]
}
],
'xmlns:soapenv' => 'http://schemas.xmlsoap.org/soap/envelope/'
};
I am using below code :
#!/usr/bin/env perl
use strict;
use Data::Dumper;
use XML::Simple qw(:strict);
my $data = XMLin('Refund.xml', forcearray=>1, keyattr=>[] );
print "Reference type in data is : ", ref($data), "\n";
print Dumper($data);
#Try to access the values
my $records=$data->{"soapenv:Body"}->[0]->{"ns1:queryResponse"}->[0]->{"ns1:result"}->
[0]->{"ns1:records"};
foreach my $record ( #{ $records } ) {
print $record->{"ns2:RefundTransactionTime"};
print "\n";
}
print Dumper($data) generates hash reference containing array of hashes.
I want to format the hash reference generated above to the array of array references format as shown below:
[
[
"AccountId",
"Id",
"Amount",
"CreatedDate",
"Comment",
"Status",
"Type",
"RefundDate",
"MethodType"
],
[
"2c92c0f8410f4d8701411411a9ad79b7",
"2c92c0f8410f4d9a0141145bfbb61a9b",
"477.74",
"2013-09-12T15:47:54.000-08:00",
"16 Payment Exception - Chargeback",
"Processes",
"External",
"2013-10-12T00:00:00",
"CreditCard"
],
[
"2c92c0f9410f55ee0141132b6c936e15",
"2c92c0f8410f4d9a014113d2170a2e17",
"99",
"2013-09-12T13:17:17.000-08:00",
"",
"Processed",
"Electronic",
"2013-09-12T00:00:00",
"Chase Paymentech"
]
],
Any help is greatly appreciated. Thank you
The return value ARRAY(0x1f9fca98) indicates that $record->{"ns2:RefundTransactionTime"} contains an array reference. You probably want the first element: $record->{"ns2:RefundTransactionTime"}->[0].
I want to remove values which contain "local" string in their value. here is my hash output (print Dumper ($hash)):
$VAR1 = {
'FARM_03' => [
'nfs01',
'nfs02',
'nfs03',
'localvmfs',
'localvmfs'
],
'FARM_07' => [
'nfs01',
'localvmfs',
'localvmfs'
],
'FARM_11' => [
'nfs01',
'localvmfs',
'localvmfs'
]
};
Hence I wrote below code in my script to omit the "local" entries:
foreach my $key ( keys %$hash )
{
#{ $hash->{key} } = grep { !/local/i } #{ $hash->{key} };
}
and here is the output after running above grep command:
$VAR1 = {
'FARM_03' => [
'nfs01',
'nfs02',
'nfs03',
'localvmfs',
'localvmfs'
],
'FARM_07' => [
'nfs01',
'localvmfs',
'localvmfs'
],
'FARM_11' => [
'nfs01',
'localvmfs',
'localvmfs'
]
'key' => []
};
It is not removing the "local" entries as well as it adds a new field 'key' => [].
Could you tell me what is wrong with my grep statement.
Thanks.
You have {key} where you mean {$key} (twice).