I have the following code:
print Dumper($dec_res->{repositories}[0]);
print Dumper($dec_res->{repositories}[1]);
my #repos = ($dec_res->{repositories});
print scalar #repos . "\n";
and the output is the following:
$VAR1 = {
'status' => 'OK',
'name' => 'apir',
'svnUrl' => 'https://url.whatever/svn/apir',
'id' => 39,
'viewvcUrl' => 'https://url.whatever/viewvc/apir/'
};
$VAR1 = {
'status' => 'OK',
'name' => 'CCDS',
'svnUrl' => 'https://url.whatever/svn/CCDS',
'id' => 26,
'viewvcUrl' => 'https://url.whatever/viewvc/CCDS/'
};
1
So my question is why $dec_res->{repositories} is clearly an array but #repos is not?
Here I printed the size but even trying to access elements with $repos[0] still returns an error.
Dumping $repos[0] actually print the whole structure... like dumping $dec_res->{repositories}
$dec_res->{repositories} is clearly an array
It isn't. It is an array reference.
but #repos is not?
It is an array.
You are creating a list that is one item long, and that item is the array reference. You then assign the list to the array, so the array holds that single item.
You need to dereference the array instead.
my #repos = #{$dec_res->{repositories}};
perlref explains more about references in Perl.
Related
Dear fellow perl programmers,
I wanted to access to this array
my #vsrvAttribs = qw(
Code
Description
vsrv_id
vsrv_name
vsrv_vcpu_no
vsrv_vmem_size
vsrv_vdspace_alloc
vsrv_mgmt_ip
vsrv_os
vsrv_virt_platf
vsrv_owner
vsrv_contact
vsrv_state
);
through a variable composed of a variable and a string suffix, which of course led to the error message like this
Can't use string ("#vsrvAttribs") as an ARRAY ref while "strict refs" in use at cmdbuild.pl line 262.`
Therefore I decided to get the reference to the array through a hash
my %attribs = ( vsrv => #vsrvAttribs );
And this is the code where I need to get the content of aforementioned array
foreach my $classTypeKey (keys %classTypes) {
my #attribs = $attribs{$classTypeKey};
print Dumper(\#attribs);
}
It seems I can get the reference to the array #vsrvAttribs, but when I checked the content of the array with Dumper , the array have got only one element
$VAR1 = [
'Code'
];
Do you have any idea where could be the problem?
How do you store the array in a hash and access it later?
You need to store your array by reference like this:
my %attribs = ( vsrv => \#vsrvAttribs );
Note the backslash before the # sigil. This tells perl that you want a reference to the array.
Then when access the array stored in $attribs{vsrv} you need to treat it as a reference instead of as an array. You'll do something like this:
foreach my $classTypeKey (keys %classTypes) {
# make a copy of the array by dereferencing
my #attribs = #{ $attribs{$classTypeKey} };
# OR just use the array reference if profiling shows performance issues:
my $attribs = $attribs{$classTypeKey}
# these will show the same thing if you haven't done anything to #attribs
# in the interim
print Dumper(\#attribs);
print Dumper($attribs);
}
Why did you only get one value and where did the rest of the array go?
Your missing values from #vsrvAttribs weren't lost they were assigned as keys and values to %attribs itself. Try adding the following just after you made your assignment and you'll see it for yourself:
my %attribs = ( vsrv => #vsrvAttribs );
print Dumper(\%attribs);
You'll see output like this:
$VAR1 = {
'vsrv_contact' => 'vsrv_state',
'vsrv_virt_platf' => 'vsrv_owner',
'vsrv' => 'Code',
'vsrv_name' => 'vsrv_vcpu_no',
'vsrv_mgmt_ip' => 'vsrv_os',
'Description' => 'vsrv_id',
'vsrv_vmem_size' => 'vsrv_vdspace_alloc'
};
This is because perl interpreted your assignment by expanding the contents #vsrvAttribs as multiple arguments to the list literal ():
my %attribs = (
# your key => first value from array
vsrv => 'Code',
# subsequent values of the array
Description => 'vsrv_id',
vsrv_name => 'vsrv_vcpu_no',
vsrv_vmem_size => 'vsrv_vdspace_alloc',
vsrv_mgmt_ip => 'vsrv_os',
vsrv_virt_platf => 'vsrv_owner',
vsrv_contact => 'vsrv_state',
);
This is legal in perl and there are reasons where you might want to do this but in your case it wasn't what you wanted.
Incidentally, you would have been warned that perl was doing something that you might not want if you had an even number of elements in your array. Your 13 elements plush the hash key "vsrv" makes 14 which is even. Perl will take any list with an even number of elements and happily make it into a hash. If your array had another element for 15 elements total with the hash key you would get a warning: Odd number of elements in hash assignment at foo.pl line 28.
See "Making References" and "Using References" in perldoc perlreftut for more information.
If you use a bare array in a hash definition like
my %attribs = ( vsrv => #vsrv_attribs )
the array is expanded and used as key/value pairs, so you will get
my %attribs = (
vsrv => 'Code',
Description => 'vsrv_id',
vsrv_name => 'vsrv_vcpu_no',
vsrv_vmem_size => 'vsrv_vdspace_alloc',
...
)
The value of a Perl hash element can only be a scalar value, so if you want an array of values there you have to take a reference, as shown below
It is also a bad idea to use capitals in Perl identifiers for anything except globals, such as package names. Local names are conventional lower-case alphanumeric plus underscore, so $class_type_key instead of $classTypeKey
use strict;
use warnings;
use Data::Dumper;
my #vsrv_attribs = qw(
Code
Description
vsrv_id
vsrv_name
vsrv_vcpu_no
vsrv_vmem_size
vsrv_vdspace_alloc
vsrv_mgmt_ip
vsrv_os
vsrv_virt_platf
vsrv_owner
vsrv_contact
vsrv_state
);
my %attribs = (
vsrc => \#vsrv_attribs,
);
for my $class_type_key (keys %attribs) {
my $attribs = $attribs{$class_type_key};
print Dumper $attribs;
}
output
$VAR1 = [
'Code',
'Description',
'vsrv_id',
'vsrv_name',
'vsrv_vcpu_no',
'vsrv_vmem_size',
'vsrv_vdspace_alloc',
'vsrv_mgmt_ip',
'vsrv_os',
'vsrv_virt_platf',
'vsrv_owner',
'vsrv_contact',
'vsrv_state'
];
How can I Iterate over this ridiculously tedious array?
array (size=6)
0 =>
array (size=1)
'Question' =>
array (size=2)
'id' => string 'q_1' (length=3)
'question_desc' => string 'Is this correct?)' (length=15)
1 =>
array (size=1)
'Question' =>
array (size=2)
'id' => string 'q_10' (length=4)
'question_desc' => string 'Do you weigh less than 45 kilograms OR more than 160 kilograms.' (length=63)
This is a var_dump from a Session data! I need to get the question_desc field from each 'Question' array object.
This array has a purpose to its structure, but I understand your frustration as I shared it before I rtfm-ed!
$flattened_data = array();
foreach($your_main_array as $question)
{
foreach($question['Question'] as $question_param)
{
if($question_param == 'question_desc')
{
$flattened_data[] = $question_param;
// if you want to be really cool you can do this instead
// this will list the array with the question id as the key.
// $flattened_data[$question[id]] = $question_param;
}
}
}
// now flattened data has only what you require
return $flattened_data;
Cakes data form makes a lot more sense once you understand its ORM and how it uses model relations. Its actually a powerful tool for managing your data, but before you need all of that power it does seem like an encumbrance for simple tasks.
The background
I got a Perl module which utilizes an array for its input/output parameters, like this:
Execute({inputfile => $req->{modules}.'filename', param => \#xchange});
Inside the module a hash is build and returned via reference
$param[0] = \%values;
This is all fine and good (I think) and print Dumper #xchange[0]; will output my desired content as
$VAR1 = { '33' => 'Title1', '53' => 'Title2', '21' => 'Title3' };
The goal
I would like to loop over the content and print the key/value pairs one by one, for example like this
%testhash = ('33' => 'Test1', '53' => 'Test2', '21' => 'Test3' );
foreach $key (keys %testhash) {
print "LOOP: $key, value=$testhash{$key}\n";
}
This loop does work as intended and dumping my testhash via print Dumper \%testhash; outputs the same as the array element above
$VAR1 = { '33' => 'Test1', '53' => 'Test2', '21' => 'Test3' };
The problem
The trouble now seems to be that although both structures appear to be of the same kind I cant get my head arround, how to properly access the returned hash which is stored inside #xchange[0].
I did try %realhash = #xchange[0]; and %realhash = \#xchange[0];, but then print Dumper \%realhash; will output $VAR1 = { 'HASH(0xa7b29c0)' => undef }; or $VAR1 = { 'REF(0xa7833a0)' => undef }; respectively.
So I either need a way to get the content of #xchange[0] inside a clean new hash or a way to foreach loop over the hash inside the #xchange[0] element.
I guess I am getting screwed by the whole hash reference concept, but I am at a loss here and can't think of another way to google for it.
$xchange[0] is a hash reference. Use the dereference operator %{...} to access it as a hash.
%realhash = %{$xchange[0]};
#xchange[0] is a scalar value, it contains the reference to a hash. When you assign it to a hash
%hash = #xchange[0];
The reference is stringified into something like HASH(0xa7b29c0), and you get the warnings
Scalar value #xchange[0] better written as $xchange[0] at ...
Reference found where even-sized list expected at ...
That is to say, you get these warnings, unless you have been so foolish as to not turn warnings on with use warnings.
The first one means what it says. The second one means that the list you assign to a hash should have an even number of elements: one value for every key. You only passed a "key" (something that Perl took as a key). The value then becomes undef, as noted in your Data::Dumper output:
$VAR1 = { 'HASH(0xa7b29c0)' => undef }
What you need to do is dereference the reference.
my $href = $xchange[0];
my %hash = %$href; # using a transition variable
my %hash2 = %{ $xchange[0] } # using support curly braces
perldsc
use warnings;
use strict;
use Data::Dumper;
$Data::Dumper::Sortkeys=1;
my %testhash = ('33' => 'Test1', '53' => 'Test2', '21' => 'Test3' );
# Add hash as first element of xchange AoH
my #xchange = \%testhash;
# Derefererence 1st element of AoH as a hash
my %realhash = %{ $xchange[0] };
# Dump new hash
print Dumper(\%realhash);
__END__
$VAR1 = {
'21' => 'Test3',
'33' => 'Test1',
'53' => 'Test2'
};
Total newbie with smarty; I did try to research before asking but couldn't figure this out. My application provides me with a nested array structure like this:
custom_fields => Array (2)
0 => Array (6)
name => "key-specs"
type => "textarea"
code => "key-specs"
value => "here are some product specifications"
sd => "05/01/2013 - 09:25:44 PM"
titled => ""
1 => Array (6)
name => "key-specs-li-class"
type => "dropdownlist"
code => "key-specs-li-class"
value => "flg4_icon"
sd => "05/01/2013 - 09:25:44 PM"
titled => ""
Instead of looping through the array, I'd really like to simply reference the outer array by the "name" dimension within the inner array. For example, I know that the name "key-specs" exists somewhere in the array, and all I want to do is quickly retrieve the "value" string that resides at the same index.
Any way to do this type of direct referencing, or am I going to have to loop?
I have an Array of Hashes in a Hash that looks like this:
$VAR1 = {
'file' => [
{
'pathname' => './out.log',
'size' => '51',
'name' => 'out.log',
'time' => '1345799296'
},
{
'pathname' => './test.pl',
'size' => '2431',
'name' => 'test.pl',
'time' => '1346080709'
},
{
'pathname' => './foo/bat.txt',
'size' => '24',
'name' => 'bat.txt',
'time' => '1345708287'
},
{
'pathname' => './foo/out.log',
'size' => '75',
'name' => 'out.log',
'time' => '1346063384'
}
]
};
How can I iterate through these "file entries" in a loop and access its values? Is it easier to copy my #array = #{ $filelist{file} }; so i only have an array of hashes?
No need to copy:
foreach my $file (#{ $filelist{file} }) {
print "path: $file->{pathname}; size: $file->{size}; ...\n";
}
There are no arrays of hashes in Perl, only arrays of scalars. It only happens that there's a bunch of syntactic sugar in case those scalars are references to arrays or hashes.
In your example, $VAR1 holds a reference to a hash containing a reference to an array containing references to hashes. Yeah, that's quite a lot of nesting to deal with. Plus, the outer hash seems kinda useless, since it contains only one value. So yes, I think giving the inner array a meaningful name would definitely make things clearer. It's not actually a "copy": only the reference is copied, not the contents. All of the following are equivalent:
my #files = $VAR1 -> {file} # dereferencing with the -> operator
my #files = ${$VAR1}{file} # derefencing with the sigil{ref} syntax
my #files = $$VAR1{file} # same as above with syntactic sugar
Note that when using the sigil{ref} syntax, the sigil obeys the same rules as usual: %{$ref} (or %$ref) is the hash referenced by $ref, but the element of %{$ref} for a given key is ${$ref}{key} (or $$ref{key}). The braces can contain arbitrary code returning a reference, while the short version can only be used when a scalar variable already holds the reference.
Once your array of references to hashes is in a variable, iterating over it is as easy as:
for (#files) {
my %file = %$_;
# do stuff with %file
}
See: http://perldoc.perl.org/perlref.html