deleting elements in array in a loop Perl - arrays

I want to make a loop where I go through all the elements in one array (#array1), and if the same element is found in another array (#array2), I want the value from a third array (#array3) with the same index to be added to the first array and deleted from the third array. I tried it this way, however the line with the if-statement runs on unitialized values, and it loops forever.
foreach my $elem1 (#array1){
my $i = 0;
while ($i < scalar #array2){
if($array2[$i]==$elem1){
push (#array1, $array3[$i]);
delete $array2[$i];
}
else{
$i++;
}
}
}

The problem is you do not increment $i if the element matches. Delete the else.

Well, here's one way to deal with the problem (to the extent that I understand what you want to do). Whenever you need to answer questions about membership, you probably want to use a hash.
use strict;
use warnings;
my #array1 = ( 11, 22, 33, 44);
my #array2 = ( 11, 2, 3, 44, 5, 44);
my #array3 = (-11, -2, -3, -44, -5, -444);
# Keep track of every value in #array1.
my %vals = map { $_ => 1 } #array1;
my #keep_indexes;
# Go through #array2, either adding the corresponding
# value in #array3 to #array1 or storing the array index.
for my $i (0 .. $#array2) {
if ($vals{$array2[$i]}){
push #array1, $array3[$i];
}
else {
push #keep_indexes, $i;
}
}
# Rebuild #array3 from the indexes that we stored.
# Change this to #array2 if you want to delete from that array instead.
#array3 = map $array3[$_], #keep_indexes;
print join(' ', #array1), "\n"; # 11 22 33 44 -11 -44 -444
print join(' ', #array2), "\n"; # 11 2 3 44 5 44
print join(' ', #array3), "\n"; # -2 -3 -5
I dislike that code, so here are a few caveats:
Any time you have numbered variable names (#array1, #array2, etc.), you're headed for confusion. You need better variable names or, more likely, a better data structure.
Any time you find yourself in the business of maintaining parallel arrays, you should consider whether a better data structure would help.

You are deleting an element from array2, not array3 as stated in the question. I think the delete operation sets the array element to undef. The next time around the loop it checks that same element that is now undef against $elem. Hence the error. Then it does he same again and again.

Related

Adding elements to an array in Perl

I have this code where I want to add 10, 11 and 12 to array arr.
my #num=(0,1,2);
my $i=10;
for my $d (#num){
if (defined($d)) {
my #arr;
$arr[$d] = $i;
$i=$i+1;
my $dvv=dump(\#arr);
print "**** $dvv \n";
}
}
The output is:
**** [10]
**** [undef, 11]
**** [undef, undef, 12]
Why is only the last element of array defined?
AntonH's answer addresses the specific problem with your specific code, but there are actually ways to rewrite your code that would avoid the problem entirely. A more "Perlish" way to accomplish the same thing would be:
my #arr;
for my $i (0 .. 2) {
push(#arr, $i + 10);
}
Or:
my #arr = map { $_ + 10 } 0 .. 2;
Or just:
my #arr = 10 .. 12;
Since you have the declaration of the array within the loop, it will re-create it each time, removing any values that would have been placed in it on previous iterations of the loop.
You should declaure #arr before the loop if you want the values to stay:
my #arr;
for my $d (#num) {
...
}
And because of this line:
$arr[$d];
$d is the position defined by the other array (0, then 1, then 2). So it puts the value of $i in that position in the array, and puts values before to undef.

Using an array to index into another array

I have two arrays, let's call them #a1 and #a2. What I'm trying to do is obtain elements from #a2 using the values in #a1 as indices. My current attempt doesn't work properly.
foreach (#a1) {
print $a2[$_] . "at" . $_;
}
This only prints $_ but not $a2[$_].
I sense there is a trivial solution to this, but I just can't find it.
There is nothing wrong with the code you have. I have tested a small script and it works as expected. Asi i suggested in my comment, try using something like Data::Dumper to see whats in the arrays before the loop.
use strict;
use warnings;
use Data::Dumper;
my #a1 = (0..4);
my #a2 = ("a".."e");
print Dumper \#a1, \#a2;
foreach (#a1){
print $a2[$_]." at ".$_."\n";
}
OUTPUT
$VAR1 = [
0,
1,
2,
3,
4
];
$VAR2 = [
'a',
'b',
'c',
'd',
'e'
];
a at 0
b at 1
c at 2
d at 3
e at 4
there's no reason your code shouldn't work as long as the values of the first array are valid addresses in the second array. but if all you really want to do is just get the values and address of the second array, you could just do:
for my $i (0..$#a2) {
print "$i: $a2[$i]","\n";
}
$#a2 is the last element address of the array.

Emptying array inside loop while using push in PERL

I am writing a subroutine that prints an array of non redundant elements from another array.
This code is inside my subroutine.
foreach (#old_table) { push(#new_table, $_) unless ($seen{$_}++); }
print "#new_table" . "\n";
Then i call my subroutine in a loop inside my main program, for the first iteration it is OK and my new table contains one occurrence of my old table.
But after that #new_table keeps elements from past iterations and the print result is false.
I tried emptying #new_table inside my subroutine like this
#new_table = ();
foreach (#old_table) { push(#new_table, $_) unless ($seen{$_}++); }
print "#new_table" . "\n";
But then my #new_table becomes empty in all iterations except for the first one.
What is the problem with this and how can i fix it ?
Due to incorrect scoping, you're reusing the #new_table and %seen of previous passes. Create these just before the loop.
my #new_table;
my %seen;
foreach (#old_table) { push(#new_table, $_) unless ($seen{$_}++); }
print "#new_table" . "\n";
This can be simplified to
my %seen;
my #new_table = grep { !$seen{$_}++ } #old_table;
print "#new_table\n";
You can also use
use List::MoreUtils qw( uniq );
my #new_table = uniq(#old_table);
print "#new_table\n";
You are using use strict; use warnings;, right? If not, you should be. Always.
You can try uniq from List::MoreUtils to remove redundant elements.
my #new_table = uniq(#old_table);
To quote from perldoc
uniq LIST
distinct LIST
Returns a new list by stripping duplicate values in LIST. The order of elements in the returned list is the same as in LIST. In
scalar context, returns
the number of unique elements in LIST.
my #x = uniq 1, 1, 2, 2, 3, 5, 3, 4; # returns 1 2 3 5 4
my $x = uniq 1, 1, 2, 2, 3, 5, 3, 4; # returns 5

How can I use pop to print an array backwards in Perl?

I am beginning to learn Perl, but am having an issue printing an array in reverse order with the pop command. I want to make this work with pop and not with other commands like reverse, or using techniques like reverse indices. Whenever I run this loop with the array, #anotherArray = (1, 2, 3, 4, 5); I only end up getting " 5, 4, 3" as a result. Am I doing something wrong with my logic or syntax?
for ($i = 0; $i <= $#anotherArray + 1; $i++) {
if ($i == 0)
{
print "Third way: ";
}
$temp = pop #anotherArray;
print "$temp ";
}
use strict;
use warnings;
my #array = (-1,0,1..5);
while (#array) {
my $e = pop #array;
print "$e\n";
}
The problem is that the array is shrinking while you are popping from it, so the limit for $i in the loop is getting smaller.
The best way is to use a while loop to keep popping from the array until it is empty.
I'm not sure what the "Third way" part is supposed to mean, but you can just print that outside the loop.
Like this
use strict;
use warnings;
my #anotherArray = (1, 2, 3, 4, 5);
print "Third way: ";
while (#anotherArray) {
print pop #anotherArray;
}
output
Third way: 54321
Just another way of doing it, since it is Perl anyways:
use strict;
use warnings;
my #anotherArray = ( 1 .. 5 );
print pop #anotherArray while #anotherArray;
it just does what is says:
print the popped-of-element from anotherArray, as long as there is a number of elements in anotherArray.
This way of doing loops or placing conditionals like if or unless are fine for a single line of code, but highly frown upon when it is a whole BLOCK of code.

Remove elements of a Perl array

Is there an easy way to remove n consecutive elements of a Perl array (thus making it shorter in length)?
You are looking for the Perl builtin function splice, which lets you pick a starting point, number of elements to remove, and an optional replacement list.
my #array = 0 .. 9;
my #slice = splice #array, 3, 3;
say "#slice"; # prints "3 4 5"
say "#array"; # prints "0 1 2 6 7 8 9"
say 0 + #array; # prints 7
You can use splice to remove array elements.
As the other answers indicated, splice works. As an alternative approach (TIMTOWTDI, after all), if you have the upper and lower indices for the n consecutive elements that you want removed, then you can do it via grep and an array slice. For example:
use strict;
use warnings;
my #a=("a".."z");
#We will remove the letters "e" through "u"
my $lower=4;
my $upper=20;
print "$_\n" foreach(#a[grep{$_<$lower or $_>$upper}0..$#a]);
The output is:
a
b
c
d
v
w
x
y
z

Resources