I noticed that when I pass an array to my subroutine it seems like it gets encapsulated by another array (so two levels, while the initial is only one).
I know that using references to arrays is better, but I'm wondering in this specific case why it is not working as expected.
Code example:
#!/usr/local/bin/perl
use Data::Dumper;
sub testSub {
my (#arr) = (#_);
print Dumper \#arr;
}
my #testArray = ();
push #testArray, {
'key1' => 'value1',
'key2' => 'value2',
'urls' => [ 'www.example.com' ]
};
print Dumper #testArray;
foreach my $item ( #testArray ) {
my #urls = testSub( $item->{'urls'} );
}
output
$VAR1 = {
'urls' => [
'www.example.com'
],
'key1' => 'value1',
'key2' => 'value2'
};
$VAR1 = [
[
'www.example.com'
]
];
my #urls = testSub( $item->{'urls'}, 'abc' );
Result of Dumper in subrotine:
$VAR1 = [
[
'www.example.com'
],
'abc'
];
Array passed by reference. Since at the time of compilation perl did not know what will be in the scalar $item->{'urls'}.
my #urls = testSub( #{ $item->{'urls'} }, 'abc' );
Result of Dumper in subrotine:
$VAR1 = [
'www.example.com',
'abc'
];
Now the compiler expects an array and turns it into a list.
You are passing $item->{'urls'} to your subroutine
Your Data::Dumper output clearly shows that the hash element looks like this
'urls' => [ 'www.example.com' ]
When you call testSub, you are making an assignment that is equivalent to
my #arr = ( [ 'www.example.com' ] );
Your statement print Dumper \#arr passes an array reference to Dumper, so it displays
[ [ 'www.example.com' ] ]
It would help your confusion if you were consistent in calling Dumper. print Dumper #testArray passes the contents of #testArray as individual parameters (although in this case the array has only a single element) while print Dumper \#arr passes an array reference as a single parameter, and is the better choice
If you want testSub to receive a list of URLs, you must expand the array $item->{urls} into a list with #{ ... }:
my #urls = testSub( #{ $item->{'urls'} } );
Related
my code looks like this
foreach ($id->{deleteids}) {
my #separated = split('_', $_);
push #rids, $separated[0];
}
on using Data::Dumper on $id->{deleteids} i get this
$VAR1 = [
'43-173739_cdfvgbvvd',
'43-173738_sddsvfdvfd',
'43-173737_sfvdfvdfvdf',
'43-173736_svdvdfvdfvdfvfdvfd'
];
My expected output of #rids that i want
$VAR1 = [
'43-173739',
'43-173738',
'43-173737',
'43-173736'
];
but on using Data::Dumper on #rids i always get
$VAR1 = 'ARRAY(0x3210010)';
Array references have different way to be called with, And as Zdim pointed you need to use a reference \#rids to dump array using Dumper
use strict;
use warnings;
use Data::Dumper;
my $id = { # creating a similar array like yours in a hash ref
deleteids => [
'43-173739_cdfvgbvvd',
'43-173738_sddsvfdvfd',
'43-173737_sfvdfvdfvdf',
'43-173736_svdvdfvdfvdfvfdvfd'
]
};
print Dumper($id->{deleteids});
my #rids;
foreach (#{$id->{deleteids}}) { # correct way to use array ref
my #separated = split('_', $_);
push #rids, $separated[0];
}
print Dumper(\#rids); # how to dump array using Dumper
Output:
# $id hash ref
$VAR1 = [
'43-173739_cdfvgbvvd',
'43-173738_sddsvfdvfd',
'43-173737_sfvdfvdfvdf',
'43-173736_svdvdfvdfvdfvfdvfd'
];
# #rids
$VAR1 = [
'43-173739',
'43-173738',
'43-173737',
'43-173736'
];
Following piece of code should do what you expect (remove undesired part)
foreach ($id->{deleteids}) {
s/_.*//;
push #rids, $_;
}
My goal is to convert this
my #array=("red", "blue", "green", ["purple", "orange"]);
into this
my #array = ( ["red"], ["blue"], ["green"], ["purple", "orange"]);
Current test code:
my #array = ("red", "blue", "green", ["purple", "orange"] );
foreach $item ( #array ) {
#if this was Python, it would be as simple as:
# if is not instance(item, array):
# # item wasn't a list
# item = [item]
if(ref($item) ne 'ARRAY'){
#It's an array reference...
#you can read it with $item->[1]
#or dereference it uisng #newarray = #{$item}
#print "We've got an array!!\n";
print $item, "\n";
# keep a copy of our string
$temp = $item;
# re-use the variable but convert to an empty list
#item = ();
# add the temp-copy as first list item
#item[0] = $temp;
# print each list item (should be just one item)
print "$_\n" for $item;
}else{
#not an array in any way...
print "ALREADY an array!!\n";
}
}
# EXPECTED my #array=(["red"], ["blue"], ["green"], ["purple", "orange"]);
print #array , "\n";
foreach $item (#array){
if(ref($item) ne 'ARRAY'){
#
#say for $item;
print "didn't convert properly to array\n";
}
}
The comment about python maps pretty directly to perl.
my #array = ("red", "blue", "green", ["purple", "orange"] );
foreach $item ( #array ) {
#if this was Python, it would be as simple as:
# if is not instance(item, array):
# # item wasn't a list
# item = [item]
if (ref $item ne 'ARRAY') {
$item = [ $item ];
}
}
though using map as in Borodin's answer would be more natural.
I'm wondering why you want to do this, but it's
#array = map { ref ? $_ : [ $_ ] } #array
And please don't call arrays #array; that's what the # is for.
Your comment is ridiculous
#if this was Python, it would be as simple as:
# if is not instance(item, array):
# # item wasn't a list
# item = [item]
If you were familiar with Perl then you wouldn't need to ask the question. You must be aware that there is no one-to-one translation from Python to Perl. Python is much less expressive than either Perl or C, but I can't imagine you demanding a simple conversion to C.
Please get over your bigotry.
If you push the values to a new array, you don't need to do more than evaluate whether or not $item is an arrayref:
#! perl
use strict;
use warnings;
use Data::Dumper;
my #array=("red", "blue", "green", ["purple", "orange"]);
my #new_array;
foreach my $item (#array) {
if ( ref($item) eq 'ARRAY' ) {
push #new_array, $item;
}
else {
push #new_array, [$item];
}
}
print Dumper \#new_array;
Output from Dumper:
$VAR1 = [
[
'red'
],
[
'blue'
],
[
'green'
],
[
'purple',
'orange'
]
];
After a long day of learning more Perl than I ever thought/wanted to learn... here's what I think is a workable solution:
#! perl
use strict;
use warnings;
use Data::Dumper;
my %the_dict = (duts =>
{dut_a => {UDF => 'hamnet'},
dut_b => {UDF => [ '1', '2', '3', ]},
dut_c => {UDF => [ 'Z' ], }});
print Dumper \%the_dict;
foreach my $dut (keys %{$the_dict{duts}}) {
# convert the dut's UDF value to an array if it wasn't already
if ( 'ARRAY' ne ref $the_dict{duts}->{$dut}{UDF} ) {
$the_dict{duts}->{$dut}{UDF} = [ $the_dict{duts}->{$dut}{UDF} ];
}
# now append a new value to the array
push(#{$the_dict{duts}{$dut}{UDF}}, 'works');
}
print Dumper \%the_dict;
when run we see these print-outs:
$VAR1 = {
'duts' => {
'dut_a' => {
'UDF' => 'hamnet'
},
'dut_c' => {
'UDF' => [
'Z'
]
},
'dut_b' => {
'UDF' => [
'1',
'2',
'3'
]
}
}
};
$VAR1 = {
'duts' => {
'dut_a' => {
'UDF' => [
'hamnet',
'works'
]
},
'dut_c' => {
'UDF' => [
'Z',
'works'
]
},
'dut_b' => {
'UDF' => [
'1',
'2',
'3',
'works'
]
}
}
};
Given an array of keys and an array of values, I can create a hash with these keys and values using #hash{#keys} = #vals.
However, I would like to do this for subkeys of a hash. This does not work: $h{"key"}{#subkeys} = #vals.
$ perl -MData::Dumper -le '
#subkeys=(qw(one two));
#vals=(1, 2);
$hash{"key"}{#subkeys} = #vals;
for (qw(subkeys vals)) {
print "$_ :\n", Dumper(\#{$_})
};
print "hash: \n", Dumper(\%hash);'
What I get is:
subkeys :
$VAR1 = [
'one',
'two'
];
vals :
$VAR1 = [
1,
2
];
hash:
$VAR1 = {
'key' => {
'2' => 2
}
};
If this is possible, what would be the correct syntax to get the following Dumper result:
$VAR1 = {
'key' => {
'one' => 1,
'two' => 2
}
};
It does work when using a temporary hash:
perl -MData::Dumper -le '#subkeys=(qw(one two)); #vals=(1, 2); #tmp{#subkeys}=#vals; $hash{"key"}={%tmp}; print Dumper(\%hash)'
But I suspect I'm just missing the correct syntax to get it without the %tmp hash.
You need to close the hashref part in a #{} slice "cast".
#{$hash{"key"}}{#subkeys} = #vals;
I have a reference to an array of hases that I pass to a subroutine in my perl script
This is the code:
sub mySub {
(my $resultref) = #_;
my #list = #$resultref;
print Dumper(#list);
foreach my $result (#list) {
print Dumper($result);
}
}
And this is the output:
$VAR1 = [
{
'portName' => '1.1',
'ips' => [
'192.168.1.242'
],
'switchIp' => '192.168.1.20',
'macs' => [
'00:16:76:9e:63:47'
]
},
{
'portName' => '1.10',
'ips' => [
'192.168.1.119',
'192.168.1.3'
],
'switchIp' => '192.168.1.20',
'macs' => [
'd0:67:e5:f8:7e:7e',
'd0:67:e5:f8:7e:76'
]
},
];
$VAR1 = [
{
'portName' => '1.1',
'ips' => [
'192.168.1.242'
],
'switchIp' => '192.168.1.20',
'macs' => [
'00:16:76:9e:63:47'
]
},
{
'portName' => '1.10',
'ips' => [
'192.168.1.119',
'192.168.1.3'
],
'switchIp' => '192.168.1.20',
'macs' => [
'd0:67:e5:f8:7e:7e',
'd0:67:e5:f8:7e:76'
]
},
];
The loop is putting the whole array into the $result variable. I have tried dereferencing it as #$result[0] with no success.
How do I loop those hashes individually?
Thanks!
The arguments to Data::Dumper's Dumper function should be references. E.g.:
use Data::Dumper;
my #array = ([1,2,3], [11,22,33]); # Two-dimensional array
print Dumper #array; # print array
print Dumper \#array; # print reference to array
The output:
$VAR1 = [
1,
2,
3
];
$VAR2 = [
11,
22,
33
];
$VAR1 = [
[
1,
2,
3
],
[
11,
22,
33
]
];
The second print gives us the entire structure in one variable. When you print the array directly, it expands into all its elements, so...
print Dumper #array;
Is equivalent to:
print Dumper $array[0], $array[1], ..., $array[$#array];
So, in your case, just do:
sub mySub {
my ($resultref) = #_;
print Dumper $resultref;
}
Accessing the inner variables:
Just take a look at Data::Dumper's output:
$VAR1 = [ # bracket denotes start of an array ref
{ # curly brackets = hash ref
'portName' => '1.1',
'ips' => [
'192.168.1.242'
],
'switchIp' => '192.168.1.20',
'macs' => [
'00:16:76:9e:63:47'
]
}, # hash ref ends, comma = new array element begins
{ # new hash ref
'portName' => '1.10',
'ips' => [
'192.168.1.119',
'192.168.1.3'
],
'switchIp' => '192.168.1.20',
'macs' => [
'd0:67:e5:f8:7e:7e',
'd0:67:e5:f8:7e:76'
]
}, # end of hash
]; # end of array
Important to note here is that all elements of an array, and all the values of a hash are scalars. Therefore, all hashes and arrays can easily be broken up into a list of scalars.
for my $aref (#$resultref) { # starting array ref
for my $aref2 (#$aref) { # second level array ref
for my $href (#$aref2) # here begins the hash
local $\ = "\n"; # add newline to print for simplicity
print $href->{portName}; # printing a scalar
print for #{$href_>{ips}}; # printing an array ref w post-script loop
print $href->{switchIp};
print for #{$href->{macs}};
}
}
}
Note the use of the arrow operator to dereference a reference. If you have a hash or array you would do $array[0] or $hash{$key}, but by using a reference, you "point" to the address contained in the reference instead: $array->[0] or $hash->{$key}.
The parameter passed to mySub is a reference to an array of arrayrefs. To iterate over the nested arrays you could do:
sub mySub {
my ($resultref) = #_;
for my $result (#$resultref) {
my #list = #$result; # array of hashrefs
...
}
}
I have a reference to an array of hases
No, you've passed in a reference to an array of references to an array of hashes.
If you remove that outer level of indirection then your code works as desired.
I have this code that works
my #new = keys %h1;
my #old = keys %h2;
function(\#new, \#old);
but can it be done without having to declare variables first?
function must have its arguments as references.
use strict;
use Data::Dumper;
my %test = (key1 => "value",key2 => "value2");
my %test2 = (key3 => "value3",key4 => "value4");
test_sub([keys %test], [keys %test2]);
sub test_sub{
my $ref_arr = shift;
my $ref_arr2 = shift;
print Dumper($ref_arr);
print Dumper($ref_arr2);
}
Output:
$VAR1 = [
'key2',
'key1'
];
$VAR1 = [
'key4',
'key3'
];
function([ keys %h1 ], [ keys %h2 ]);
From perldoc perlref:
A reference to an anonymous array can
be created using square brackets:
$arrayref = [1, 2, ['a', 'b', 'c']];