Foreach array with Perl - arrays

It seems my code cannot iterate over an array stored in a hash.
What did I miss ?
#!/usr/bin/env perl
use Data::Dumper;
my $data = {array => ['a', 'b', 'c']};
my #array = $data->{array};
print Dumper(#array); # It looks like $data->{array} is an array
print "Ref: ".ref($data->{array})."\n"; # And this array is indeed an array
foreach ($data->{array}) { print "$_\n"; } # But this doesn't work
foreach (#array) { print "$_\n"; } # Neither this one
# But with a regular array it works...
my #myNames = ('Larry', 'Curly', 'Moe');
foreach (#myNames) { print "$_\n"; }
My output:
$VAR1 = [
'a',
'b',
'c'
];
$VAR1 = 'a';
Ref: ARRAY
ARRAY(0x8002bcf8)
ARRAY(0x8002bcf8)
Larry
Curly
Moe
I am pretty confused with REF/SCALAR types. Sometime Perl takes values as references sometime not. In this case, because I get 'ARRAY' from the ref function, I guess $->{array} doesn't give me an array but a reference to the array.
I have also tried #$data->{array} without success.

$data->{array} is indeed an array reference.
To dereference it, use #{} on the reference
foreach (#{$data->{array}}) { print "$_\n"; }
Edit:
Or if you dont want to use {...} after #
my $arrayref = $data->{array};
foreach (#$arrayref ) { print "$_\n"; }

Related

Perl - Apply grep to AoA and get particular element from already returned array

I have 2D array and I want to filter by specific index but return another index. I have tried this:
print (grep {$_->[0] eq "defuser"} #passwd_file_AoA)[2];
But gives me syntax error.
EDIT:
From this array
#AoA = ([1,"a","b"],[2,"c","d"]);
I want to get
"a"
I have tried following based on bellow suggestions but returns me 1
use Data::Dumper;
#AoA = ([1,"a","b"],[2,"c","d"]);
$a = map { $_->[1] } grep { $_->[0] == 1 } #AoA;
print Dumper $a
If you want to create a brand new array with the values from your array of arrays, you can use map and grep:
my #new_arr = map { $_->[2] } grep { $_->[0] eq "defuser" } #AoA;
If you want to print AND make a new array:
my #new_arr = map { say $_->[2]; $_->[2] } grep { $_->[0] eq "defuser" } #AoA;
(uses say, which requires perl 5.10 or greater and use feature ':5.10', but saves you having to type out \n every time you print to STDERR)
As ThisSuitIsBlackNot says, printing can be done with
for my $a (#AoA) {
say $_->[2] if $_->[0] eq 'defuser';
}

How to pass an array as value to a single key in a hash of perl?

my %hash;
my #chain;
foreach (my $i=0; $i<=7; $i++)
{
foreach (my $j=0; $j<=($#output); $j++)
{
if ($output[$j] =~ /chain1/)
{
push (#array, $output[$j]);
}
}
$hash{$chain[$i]} = [ #array ];
}
print "$hash{$chain[0]}\n";
The problem is I am not able to assign the arrays to unique keys in the hash. when I say print all the keys print the same output.
You keep adding to the same array.
for (...) {
{
my #array; <-- Add here
for (...) {
...
push #array, $output[$j];
...
}
$hash{$chain[$i]} = \#array; <-- No need to copy elements anymore.
}
Perl hash are designed to hold only scalar values. It can have a key and the value can be the address reference of the array (which is scalar). But if the array value need to be modified concatenate the contents of the array as a string with certain delimiter and store the string as key.
Hope this Helps.

Can't print contents of hash

Here's how I've set up my hash:
my #keys_i_need = qw(A B C D E F G);
my %keys_i_need = map {$_ => []} #keys_i_need;
foreach my $line (#{$file_arr_ref}) {
my $sub = substr( $line, 0, 1);
if(($sub ne "#") and ($sub ne "")){
my #key_vals = split(/\s+/, $line);
my $key = shift #key_vals;
if(exists $keys_i_need{$key}) {
INFO("key is $key value is " . join(", ", #key_vals));
push (#{$keys_i_need{$key}}, \#key_vals);
DEBUG(Dumper \%keys_i_need);
}
}
}
If I understand this correctly, it's a hash, where each value is an array reference with array references inside the array reference. I don't want to use Dumper because I want to pick out each piece.
I'm trying to read out what's been pulled into the hash but I'm getting an error message that says:
"my" variable $values masks earlier declaration in same statement at /home/rabdelaz/workspace/akatest_5/scripts/Viper/Stragglers.pl line 67.
foreach my $key (keys %$config_options) {
foreach my $arr_ref_of_arr_values (%$config_options{$key}) {
foreach my $values (#$arr_ref_of_arr_values) { #<----------line 67
foreach my $value (#$values) {
INFO("key $key has values $value");
}
}
}
}
This looks right to me. I can't quite figure out what perl is complaining about. Any thoughts?
I see what I did now.
I was confused (once again) by the fact that an array reference is a scalar and not an array. Although, as you can see from my original code I went the wrong direction any way (outside to the hash, rather than inside to the array ref. So my final code should look more like this:
foreach my $key (keys %$config_options) {
foreach my $arr_ref_of_arr_values ($$config_options{$key}) {
foreach my $values (#$arr_ref_of_arr_values) { #<----------line 67
foreach my $value (#$values) {
INFO("key $key has values $value");
}
}
}
}
In the course of debugging, I ended up with this to get the first value of the first array in the first key:
INFO("first value of first array of first key: " . ${${$$config_options{'A'}}[0]}[0]);
I then used this as my guidepost.

Perl - Building array of arrays leads to overflow

I am trying to analyze a hashmap, for duplicate values and getting their keys in arrays. These arrays will be in an array of arrays. I am a newbie, by the way. However it never stops running when I start it. Where am I wrong?
while (($k,$v)=each %hashmap){
$hasduplicate = 0;
delete $hashmap{$k};
#dups = ();
while (($k1,$v1) = each %hashmap){
if ($v1 eq $v) {
$hasduplicate = 1;
push #dups, $k1;
delete $hashmap{$k1};
}
}
if ($hasduplicate){
push (#dups, $k);
push #dupsarray, [#dups];}
}
Each hash has just one iterator aligned to itself in Perl (see each). Therefore, running each for the same hash in a loop that calls each is not doing what you think.
If you want to see what's going on, try adding the following line at the start of the outer loop:
warn $k;
You are missing several dollar signs before variable names. For example, you probably want to delete $hashmap{$k} instead of $hashmap{k}, which is equivalent to $hashmap{'k'}.
To output an array of arrays, you have to dereference the inner arrays:
print map "#$_\n", #dupsarray;
BTW, I would use a hash of arrays to solve your task. Here's how:
my %dups;
while (my ($k, $v) = each %hashmap) {
push #{ $dups{$v} }, $k;
}
for my $k (grep #{ $dups{$_} } > 1, keys %dups) {
print "$k: #{ $dups{$k} }\n";
}
The problem is that there can be only one each sequence per hash, as there is only a single index to keep track of the next key/value pair.
In addition, you are using k and k1 in a few places where you mean $k and $k1. You must always use strict and use warnings at the top of every Perl program. This would have alerted you to the problem.
You can get around this problem by using for my $k1 (keys %hashmap) { ... } for the inside loop. This will create a separate list of keys to assign to $k1 in turn so that there is no multiple use of the iterator.
This modification of your code does what I think you want.
use strict;
use warnings;
my %hashmap = (
a => 'a',
b => 'b',
c => 'a',
d => 'c',
);
my #dupsarray;
while (my ($k, $v) = each %hashmap) {
my $hasduplicate = 0;
delete $hashmap{$k};
my #dups;
for my $k1 (keys %hashmap) {
my $v1 = $hashmap{$k1};
if ($v1 eq $v) {
$hasduplicate = 1;
push #dups, $k1;
delete $hashmap{$k1};
}
}
if ($hasduplicate) {
push(#dups, $k);
push #dupsarray, [#dups];
}
}
use Data::Dump;
dd \#dupsarray;
output
[["a", "c"]]
A much simpler method is to create an inverted hash where the keys and values of the original hash are swapped. Then just pick out the values of the inverted hash that have more than one element. This program demonstrates
use strict;
use warnings;
my %hashmap = (
a => 'a',
b => 'b',
c => 'a',
d => 'c',
);
my #dupsarray = do {
my %inverted;
while (my ($k, $v) = each %hashmap) {
push #{ $inverted{$v} }, $k;
}
grep { #$_ > 1 } values %inverted;
};
use Data::Dump;
dd \#dupsarray;
output
[["c", "a"]]

print first row of a 2d Array in Perl

I have the below code and I'm attempting to print out only the first row of this 2d array
# how many columns
for (my $c = 0; $c <= $#list[0]; $c++) {
print $list[0][$c]."\n";
the data should be something like
[0] => "ID,Cluster,Version"
[1] => "2,32,v44"
The Error:
syntax error at ./connect_qb.pl line 107, near "$#list["
syntax error at ./connect_qb.pl line 107, near "++) "
Execution of ./connect_qb.pl aborted due to compilation errors.
$list[0]
is a reference to an array, so the array is
#{ $list[0] }
so the last element of that array is
$#{ $list[0] }
so you'd use
for my $c (0 .. $#{ $list[0] }) {
print "$list[0][$c]\n";
}
or
for (#{ $list[0] }) {
print "$_\n";
}
You should avoid c-style for loops. Here's one way to do it.
use strict;
use warnings;
use feature qw(say);
my #a = (["ID","Cluster","Version"], ["2","32","v44"]);
say for (#{$a[0]});
A slightly less confusing dereferencing:
...
my $ref = $a[0];
say for (#$ref);
Here is a simple one liner for that
print join(",",#{$list[0]}),"\n";
Try this:
for (my $c = 0; $c <= (scalar #{$list[0]}); $c++)
for the loop condition

Resources