Perl- Iterating through an array of hashes with square brackets - arrays

I have a hash whose keys is a string and key is array(the complication is that the array is defined in the square bracket), So my hash is like this..
model = {
'add' => [
{'name' => 'abc1', 'value' => 'def' },
{'name' => 'abc2', value => 'ghi'}
],
'remove' => [
{'name' => 'abc1', 'value' => 'def' },
{'name' => 'abc2', value => 'ghi'}
]
};
So what I am trying to achive is that when I try to iterate through the hashes of array
model->{add} as
print $_->{name} foreach(model->{add})
it doesnt work.
I guess this is because the array is in [] instead of ().
Considering the input cannot be changed. please let me know how to get through this...

$model = { 'add' => [ {'name' => 'abc1', 'value' => 'def' },
{'name' => 'abc2', 'value' => 'ghi'} ],
'remove' => [ {'name' => 'abc1', 'value' => 'def' },
{'name' => 'abc2', 'value' => 'ghi'} ] };
print $_->{name} foreach( #{ $model->{add} } );

You have a 3-level nested structure: A HashRef containing ArrayRefs containing HashRefs.
my $model = {
'add' => [
{
'name' => 'abc1',
'value' => 'def'
}, {
'name' => 'abc2',
value => 'ghi'
}
],
'remove' => [
{
'name' => 'abc1',
'value' => 'def'
}, {
'name' => 'abc2',
value => 'ghi'
}
]
};
To access those nested arrays and hashes, you need to dereference them, by adding % or # in front of it, depending on whether it is a hash or array.
my $arrayref = $model->{add};
foreach my $hashref (#$arrayref) {
print $hashref->{name}, "\n";
}

The statement:
print $_->{name} foreach(model->{add})
Does not work because model is a bareword, not a variable. If you have these two pragmas in your code:
use strict;
use warnings;
You will not be able to make mistakes like this. warnings will tell you:
Unquoted string "model" may clash with future reserved word at ...
Name "main::model" used only once: possible typo at ...
Use of uninitialized value in print at ...
And strict will tell you:
Can't use bareword ("model") as a HASH ref while "strict refs" in use at ...
However, if you do not have those two pragmas enabled, Perl will happily print the empty string and be silent about the whole thing. Which makes the mistake rather hard to detect.
The correct way to handle this is to grab the correct scalar value from the hash, and dereference it using the correct sigil. If you look at the key 'add':
'add' => [
You'll see that it has an array reference stored in it, which means the sigil to use is #. You'll need support curly braces to disambiguate the references. Also, you have to refer to your variable as $model.
print $_->{name} for #{ $model->{add} };
Which is the same as
my $adds = $model->{add};
print $_->{name} for #$adds;

First and foremost use use strict; use warnings;
The scalar model should be written as
my $model = { 'add' => [ {'name' => 'abc1', 'value' => 'def' }, {'name' => 'abc2', value => 'ghi'} ], 'remove' => [ {'name' => 'abc1', 'value' => 'def' }, {'name' => 'abc2', value => 'ghi'} ] };
and
print $_->{name} foreach( ( #{ $model->{ add } } ) );

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;

Perl: Filter AoH output

I'm trying to sort my AoH which looks like this:
$VAR1 = [
{
'Name' => 'John',
'Lastname' => 'Derp',
'Organization' => 'Finance',
'OfficeNR' => '23',
'ID' => '145'
},
{
'Name' => 'Kate',
'Lastname' => 'Herp',
'Organization' => 'HR',
'OfficeNR' => '78',
'ID' => '35'
},
{
'Name' => 'Jack',
'Lastname' => 'Serp',
'Organization' => 'Finance',
'OfficeNR' => '23',
'ID' => '98'
}
];
What I'm trying to do is to filter my output using keys from AoH, for example print out only those who have 'Organization' => 'Finance'.
I've tried to solve it using new array:
my #SortedAoH = sort { {Organization=>{'Finance'}} } #AoH;
But it doesn't work.
What you want is grep, not sort. You are getting the basic syntax of equivalence checking wrong as well.
Anyway, the filter is:
my #finance_orgs = grep { $_->{'Organization'} eq 'Finance' } #AoH;
The #finance_orgs variable will now only include the ones with Organization set to Finance.
Just an explanation of the pieces:
The $_ variable is the variable that gets assigned whenever the value is implied in a block, such as in grep or map or in a for loop without an explicitly named variable.
$_->{'Organization'} performs a hash lookup on the hash as it iterates through each entry in your array.
eq is the operator used to test for string equivalence (as opposed to == which tests for numeric equivalence).

Find the size of an array of hash in Perl

How do you get the size of the following array of hashes?
I thought this would do it, but it did not work...
print Dumper scalar $item->{'detail'};
$VAR1 = [
{ 'content' => undef, 'name' => 'entree', 'url_name' => 'entree' },
{ 'content' => undef, 'name' => 'dessert', 'url_name' => 'desert' },
{ 'content' => undef, 'name' => 'drink', 'url_name' => 'drink' }
];
Or how can I print all the url_name (entree, desert, drink) in the array of hashes without knowing the size?
You have an array reference. To get the size of the referenced array, first dereference the reference:
print scalar #{$item->{'detail'}};
And to list the URLs:
my $v = [
{ 'content' => undef, 'name' => 'entree', 'url_name' => 'entree' },
{ 'content' => undef, 'name' => 'dessert', 'url_name' => 'desert' },
{ 'content' => undef, 'name' => 'drink', 'url_name' => 'drink' }
]; # or $v = $item->{'detail'};
foreach my $h (#$v) {
print $h->{url_name}, "\n";
}
I'm not sure why you think you need the array size in order to print the url_name values. Nonetheless, here's how it works.
use strict;
use warnings;
use Data::Dumper;
my $v = [ # note that this is a scalar value
{ 'content' => undef, 'name' => 'entree', 'url_name' => 'entree' },
{ 'content' => undef, 'name' => 'dessert', 'url_name' => 'desert' },
{ 'content' => undef, 'name' => 'drink', 'url_name' => 'drink' }
];
my $item = { detail => $v }; # recreate your structure $item->{detail}
my $size = #$v; # this is how its done with $v
my $size2 = #{ $item->{detail} }; # and with your original structure
my #x = map $_->{url_name}, #$v; # extract url_name values
print Dumper \#x;
As you see, $item->{detail} and $v are identical. When you feed this scalar value directly (through the scalar function, which does nothing in this case) to Dumper, you get the printed value seen in $v above. All that scalar does is change the context used with print and enforce a scalar context rather than list context. We can do the same thing by using scalar assignment ($size and $size2).
When using the original structure, you need to use the #{ } brackets to clarify for perl that what is inside them is an array ref.
As you see, extracting the values is easily done with a map statement. It acts as a loop, iterating over all the values in #$v (or #{ $item->{detail} }), returning for each value the statement $_->{url_name}.

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'}

How do I access array elements stored in a hash?

I'm working on a script and trying to get some values from an array stored in a hash. After searching on Google, searching for questions on SO (and there are some with similar titles but which have remained unsolved or solve problems a little bit different than mine), and after checking out the Data Structures Cookbook and trying everything reasonable to try, I've come to ask your help.
I have a hash, $action, and an array, $action->{'Events'}. Here's the output for print Dumper($action->{'Events'});:
$VAR1 = [{
'Muted' => 'something',
'Role' => 'something',
'Event' => 'something',
'Channel' => 'something',
'Talking' => 'something',
'UserNumber' => 'somenumber',
'CallerIDName' => 'somenumber',
'Conference' => 'somenumber',
'MarkedUser' => 'something',
'ActionID' => 'somenumber',
'CallerIDNum' => 'somenumber',
'Admin' => 'something'
}];
I need to get, for example, the value of $action->{'EVENTS'}->{'CallerIDName'}, but this syntax and many other won't work. I've even tried $action->{'EVENTS'}[6] and $action->{'EVENTS'}->[6] and so on.
It is Array of hashes, try this way:
$action->{'EVENTS'}[0]->{'CallerIDName'}
see perldsc for more detail.
Updated example like:
use strict;
use warnings;
use Data::Dumper;
my $action = {};
$action->{'Events'} = [{'Muted' => 'something',
'Role' => 'something',
'Event' => 'something',
'Channel' => 'something',
'Talking' => 'something',
'UserNumber' => 'somenumber',
'CallerIDName' => 'somenumber',
'Conference' => 'somenumber',
'MarkedUser' => 'something',
'ActionID' => 'somenumber',
'CallerIDNum' => 'somenumber',
'Admin' => 'something'}];
#push hash into the array of hashes
push(#{$action->{'Events'}},{'Muted' => 'something',
'Role' => 'something1',
'Event' => 'something1',
'Channel' => 'something1',
'Talking' => 'something1',
'UserNumber' => 'somenumber1',
'CallerIDName' => 'somenumber1',
'Conference' => 'somenumber1',
'MarkedUser' => 'something1',
'ActionID' => 'somenumber1',
'CallerIDNum' => 'somenumber1',
'Admin' => 'something1'} );
for(my $i=0; $i < #{$action->{'Events'}}; $i++){
print Dumper($action->{Events}[$i]); #print entire hash in array index $i
#print callerIDName key(any key) of each hash
print Dumper($action->{'Events'}[$i]->{'CallerIDName'});
}
The one you're missing is that the $action contains a reference to an array, so the next part must dereference the array. Then within that is a hash, and you need to dereference the hash. So it should look like this:
$action->{'EVENTS'}[0]{'CallerIDname'}
(note that the ->'s beyond the first are optional, so this is fine as well:
$action->{'EVENTS'}->[0]->{'CallerIDname'}
And does the exact same thing)
The [ ] on the outside indicate the hash is inside an array. So try:
$action->{Events}->[0]->{CallerIDName}
You can omit the -> between the {Events} and [0], but I prefer it for clarity. It doesn't make a difference here, but it does in other places. Compare:
#array = (1,2,3);
$arrayref = \#array;
print $arrayref[0]; # accesses non-existent array #arrayref
print $arrayref->[0]; # '1'

Resources