Perl interpreting following variable declaration - arrays

Hi all I have simple newbie question on perl. I am looking at someone's perl code and he has some variable declarations as follows:
my $OutputFiles=[];
$OutputFiles =
[
{ FID => 789133, TAGE => "BRWSGF_05K", nfiles => 1, Suffix => 'jpg' },
{ FID => 789134, TAGE => "BRWSGF_W05K", nfiles => 1, Suffix => 'jpg' },
{ FID => 789135, TAGE => "BRWSGF_E05K", nfiles => 1, Suffix => 'jpg' },
];
It seems to be some sort of array of hash but the variable declaration of $OutputFiles is not like that of an array so I am confused. Please let me know what is the variable $OutputFiles supposed to be and how can one manipulate such a variable.
Again I admit frankly that I am a rank Perl newbie.

[] creates an array reference, which is a scalar.
my $foo = [1, 2, 3];
Gives the same value of $foo as:
my #bar = (1, 2, 3);
my $foo = \#bar;
See perldoc perlref for more details.

$OutputFiles is an array reference, like Quentin's answer explained. You're also right that it is an array of hashes, but you can also think of it as an array of hash references. Here's how you can access the data:
#!/bin/perl
use strict;
use warnings;
my $OutputFiles=[];
$OutputFiles = [
{ FID => 789133, TAGE => "BRWSGF_05K", nfiles => 1, Suffix => 'jpg' },
{ FID => 789134, TAGE => "BRWSGF_W05K", nfiles => 1, Suffix => 'jpg' },
{ FID => 789135, TAGE => "BRWSGF_E05K", nfiles => 1, Suffix => 'jpg' },
];
my ($FID, $TAGE, $nfiles, $Suffix);
for my $hash_ref ( #$OutputFiles ) {
$FID = $hash_ref->{FID};
$TAGE = $hash_ref->{TAGE};
$nfiles = $hash_ref->{nfiles};
$Suffix = $hash_ref->{Suffix};
print "FID: $FID TAGE: $TAGE nfiles: $nfiles: Suffix: $Suffix\n";
}

As already mentioned by Quentin, it is array reference containing hashes, I am adding some small things how you can derefernce it & access the specific key from it as it contain hashes.
use strict;
use warnings;
use Data::Dumper;
my $OutputFiles=[];
$OutputFiles =
[
{ FID => 789133, TAGE => "BRWSGF_05K", nfiles => 1, Suffix => 'jpg' },
{ FID => 789134, TAGE => "BRWSGF_W05K", nfiles => 1, Suffix => 'jpg' },
{ FID => 789135, TAGE => "BRWSGF_E05K", nfiles => 1, Suffix => 'jpg' },
];
my #output = #{$OutputFiles}; #dereference the array ref
print Dumper(#output );
print Dumper(#{$OutputFiles}[0]->{FID}); # accessing the specific key

Related

Perl: Can you use more than two arrays in the map function?

I've been learning Perl as of late, and ran into an interesting problem. How would I go about using two arrays in map like so?
use warnings;
use strict;
use Data::Dumper;
my $names->{name} = ['Bill', 'Smith'];
my $cars->{model} = ['Honda', 'Toyota'];
my $obj = {
'Students' => [
map {
'Name' => $_,
'Model' => $_
}, #{$names->{name}}, #{$cars->{model}}
]
};
print Dumper $obj;
This will print, it's producing two too many objects than I would like.
$VAR1 = {
'Students' => [
{
'Model' => 'Bill',
'Name' => 'Bill'
},
{
'Model' => 'Smith',
'Name' => 'Smith'
},
{
'Model' => 'Honda',
'Name' => 'Honda'
},
{
'Model' => 'Toyota',
'Name' => 'Toyota'
}
]
};
What I'd like to do is have map work in a way that it'll produce these results
$VAR1 = {
'Students' => [
{
'Model' => 'Honda',
'Name' => 'Bill'
},
{
'Model' => 'Toyota',
'Name' => 'Smith'
}
]
};
use warnings;
use strict;
use Data::Dumper;
my $names->{name} = [ qw(Bill Smith) ];
my $cars->{model} = [ qw(Honda Toyota) ];
my $obj = {
Students => [
map {
{ Name => $names->{name}[$_], Model => $cars->{model}[$_] }
}
0 .. $#{$names->{name}}
]
};
print Dumper $obj;
The body of the map uses both arrays so you only need to supply indices to it.
For this both arrayrefs must be of same length and items to be paired at same indices.
The syntax $#$arrayref is for the last index in the $arrayref, and since here we have to also dereference first there's an extra pair of {}.
What you have dereferences both arrayrefs, to build a single flat list for the map, producing
my $obj = {
'Students' => [
map {
'Name' => $_,
'Model' => $_
}, qw(Bill Smith Honda Toyota);
]
};
First, this makes no sense:
my $names->{name} = ['Bill', 'Smith'];
my $cars->{model} = ['Honda', 'Toyota'];
I'll use
my #names = ( 'Bill', 'Smith' );
my #models = ( 'Honda', 'Toyota' );
We could iterate over the indexes.
map { +{ Name => $names[$_], Model => $cars[$_] } }
0..$#names
Alternatively, we could enlist zip.
use List::Util qw( zip );
map { +{ Name => $_->[0], Model => $_->[1] } }
zip \#names, \#models
This is probably better written as a while loop:
my #names = qw( Bill Smith );
my #models = qw( Honda Toyota );
my #students;
push #students, { 'name' => shift #names, 'model' => shift #models } while #names;

How to send hash of hash of array items to csv

Hash of Hash of values were printing correctly to csv but hash of hash of array values are messing up my csv file. Please find my code below which is not working.
< use strict;
use warnings;
use Data::Dumper;
my %student_hash;
my $student_report = "StudentReport.csv";
%student_hash = (
'RollNummber1' => {
'studentname' => 'Boris',
'address' => ['Vietnam',
'local'
],
'DOB' => '5june2000'
},
'RollNummber2' => {
'studentname' => 'John',
'address' => [
'4th/floor',
'Culverdown/Street',
'WestHam',
'UK.',
],
'DOB' => '2feb2000'
},
'RollNummber3' => {
'studentname' => 'Karen',
'DOB' => '5march2000'
}
);
print "StudentHash:".Dumper(\%student_hash);
open(my $fh, '>', $student_report) or die("Couldn't open ile");
print $fh "DOB \t,ADDRESS \t,NAME \t\n";
foreach my $key(keys %student_hash){
foreach my $secondkeys (keys %{$student_hash{$key}}){
if($secondkeys =~ /DOB || studentname/) {
print $fh "$student_hash{$key}{$secondkeys} \t,"; }
if($secondkeys =~ /address/) {
print $fh Dumper $student_hash{$key}{$secondkeys} };
}
print $fh "\n";
}
close($fh);>
If I comment out printing the array part then the code works fine.Can someone please suggest how to print the array elements into csv either in one cell or in multiple cells.Thanks
Use Text::CSV - you can either use print if you've got an array reference to print. (It handles quoting for you).
Or you can use column_names to set the column ordering, and print_hr to output a hash ref in that order.
It looks like you're not worried about RollNumber so you can cheat and do:
foreach my $student ( values %student_hash ) {
}
To iterate. I'm unclear what you're trying to do with address. But a simple join would probably do the trick there. (either on linefeed or whitespace, depending on the desired output).
#!/usr/bin/env perl
use strict;
use warnings;
use Text::CSV;
my %student_hash = (
'RollNummber1' => {
'studentname' => 'Boris',
'address' => [ 'Vietnam', 'local' ],
'DOB' => '5june2000'
},
'RollNummber2' => {
'studentname' => 'John',
'address' => [ '4th/floor', 'Culverdown/Street', 'WestHam', 'UK.', ],
'DOB' => '2feb2000'
},
'RollNummber3' => {
'studentname' => 'Karen',
'DOB' => '5march2000'
}
);
my $csv = Text::CSV->new( { sep_char => ',', eol => "\n" } );
$csv->column_names( 'studentname', 'DOB', 'address' );
foreach my $student ( values %student_hash ) {
if ( defined $student -> {address} ) {
$student -> {address} = join (" ", #{$student->{address}});
}
$csv->print_hr( \*STDOUT, $student );
}

Get the hash number

I am fairly new in Perl, and having worked all my life with R, there are something that I can't really can wrap my mind around.
I have an array of hashes. In all of the hashes, the keys are the same ones, but the values are different. I want to get the number of the hash that has a specific value in it, because in that hash there is another value that I want (and varies among different samples).
I don't know if this is the way that I should be addressing it, but is the one I can think of. Here is a piece of the array:
$VAR16 = {
'harmonized_name' => 'geo_loc_name',
'attribute_name' => 'geo_loc_name',
'content' => 'not determined',
'display_name' => 'geographic location'}
$VAR17 = {
'harmonized_name' => 'env_package',
'attribute_name' => 'env_package',
'content' => 'missing',
'display_name' => 'environmental package'}
In this example, I would want the 'content' value of the hash that has 'harmonized_name' = env_package
You can use grep to filter all array elements which have 'harmonized_name' = env_package, and then check their values for content,
use strict;
use warnings;
my #AoH = (
{
'harmonized_name' => 'geo_loc_name',
'attribute_name' => 'geo_loc_name',
'content' => 'not determined',
'display_name' => 'geographic location'
},
{
'harmonized_name' => 'env_package',
'attribute_name' => 'env_package',
'content' => 'missing',
'display_name' => 'environmental package'
}
);
my #result = grep { $_->{harmonized_name} eq "env_package" } #AoH;
print $_->{content}, "\n" for #result;
output
missing

Directly access value based on another value in anonymous array of hashes

Given the following anonymous array of hashes:
$AoH = [
{
'FORM_FIELD_ID' => '10353',
'VISIBLE_BY' => '10354',
'FIELD_LABEL' => 'ISINCIDENT',
'VALUE' => '',
'DEFAULT_FIELD_LABEL' => 'Yes No',
'FORM_ID' => '2113',
},
{
'FORM_FIELD_ID' => '10354',
'VISIBLE_BY' => '0',
'FIELD_LABEL' => 'CATEGORY',
'VALUE' => 'zOS Logical Security (RACF)',
'DEFAULT_FIELD_LABEL' => 'CATEGORY',
'FORM_ID' => '2113',
},
{
'FORM_FIELD_ID' => '10368',
'VISIBLE_BY' => '10354',
'FIELD_LABEL' => 'STARTDATE',
'VALUE' => '',
'DEFAULT_FIELD_LABEL' => 'REQTYPE',
'FORM_ID' => '2113',
}
];
How would I directly access the FIELD_LABEL value given that I knew the FORM_FIELD_ID is 10353?
I know I can loop through #$AoH and conditionally find $_->{FIELD_LABEL} based on $_->{FORM_FIELD_ID} == 10353, but is there anyway to directly access the wanted value if one of the other values in the same hash is known?
No, not unless you change your data structure. You could e.g. index the records by their form field id:
my %by_form_field_id = map { $_->{FORM_FIELD_ID} => $_ } #$AoH;
Then:
my $field_label = $by_form_field_id{10353}{FIELD_LABEL};
Without changing the data structure, you really have to grep:
my $field_label = (grep { $_->{FORM_FIELD_ID} == 10353 } #$AoH)[0]->{FIELD_LABEL};
You'd have to write a function loop through #array and examine the %hash or maybe use the builtin grep method:
say $_->{FIELD_LABEL} for (grep { $_->{FORM_FIELD_ID} == 10353 } #$AoH )
works. And so does this:
say %$_->{FIELD_LABEL} for (grep { $_->{FORM_FIELD_ID} == 10353 } #$AoH )
but it gives a Using a hash as a reference is deprecated warning (with pumpkin perl-5.16.3).

I don't understand this Perl Syntax, has anyone got any idea?

I have got this part from a Perl plugin. I don't understand what it does. Is it an array of associative arrays? If so, then shouldn't it be started with #? Can anyone shed some light on this issue?
my $arguments =
[ { 'name' => "process_exp",
'desc' => "{BasePlugin.process_exp}",
'type' => "regexp",
'deft' => &get_default_process_exp(),
'reqd' => "no" },
{ 'name' => "assoc_images",
'desc' => "{MP4Plugin.assoc_images}",
'type' => "flag",
'deft' => "",
'reqd' => "no" },
{ 'name' => "applet_metadata",
'desc' => "{MP4Plugin.applet_metadata}",
'type' => "flag",
'deft' => "" },
{ 'name' => "metadata_fields",
'desc' => "{MP4Plugin.metadata_fields}",
'type' => "string",
'deft' => "Title,Artist,Genre" },
{ 'name' => "file_rename_method",
'desc' => "{BasePlugin.file_rename_method}",
'type' => "enum",
'deft' => &get_default_file_rename_method(), # by default rename imported files and assoc files using this encoding
'list' => $BasePlugin::file_rename_method_list,
'reqd' => "no"
} ];
As Bwmat said it's a reference to an Array of Hash references. Read
$ man perlref
or
$ man perlreftut # this is a bit more straightforward
for if you want to know more about references.
By the way in fiew words in Perl you can do:
#array = ( 1, 2 ); # declare an array
$array_reference = \#array; # take the reference to that array
$array_reference->[0] = 2; # overwrite 1st position of #array
$numbers = [ 3, 4 ]; # this is another valid array ref declaration. Note [ ] instead of ( )
the same thing happens with hashes.
By the way in fiew words in Perl you can do:
%hash = ( foo => 1, bar => 2 );
$hash_reference = \%hash;
$hash_reference->{foo} = 2;
$langs = { perl => 'cool', php => 'ugly' }; # this is another valid hash ref declaration. Note { } instead of ( )
And... yes, you can dereference these references.
%{ $hash_reference }
will be treated as it was a hash, so if you want to print the keys of $langs above, you can do:
print $_, "\n" foreach ( keys %{ $langs } );
To dereference an array ref use #{ } instead of %{ }. Even sub can be dereferenced.
sub foo
{
print "hello world\n";
}
my %hash = ( call => \&foo );
&{ $hash{call} }; # this allows you to call the sub foo
$arguments is an array reference (a reference/pointer to an array)
You initialize arrays with () and array references with []
my #array = ( 1, 2, 3 );
my $array_ref = [ 1, 2, 3 ];
You can create a reference with \
my $other_array_ref = \#array;
When you use an array reference you will then to dereference it when using:
for my $element ( #{$array_ref} )
or
print ${$array_ref}[0];
See man perlref
Back to your question: $arguments is a reference to an array of hash references (initialized with {})
Looks like a hash of hashes reference.
you may need to dereference like
%newhash = %{$arguments}
and print the data as
print $newhash{'name'}

Resources