Replacing array elements in Perl - arrays

I'm trying to replace an element in my array and my code doesn't seem to work.
my #wholeloop = (split //, $loop);
for my $i (0 .. $#wholeloop ) {
if ( $wholeloop[$i] eq "i" ) {
$wholeloop[$i] =~ htmlinsert($offset);
$offset++
}
}
I've read about problematics of doing stuff while iterating through an array, and maybe is there a better solution. I'm trying to replace specific occurences of a character in a string, and array seemed as a reasonable tool to use.

Typically - when iterating on a loop, you don't need to do it via:
for ( 0..$#array) {
Because
for ( #array ) {
will do the same thing, but with an added advantage of $_ being an alias to the array variable.
for my $element ( #wholeloop ) {
if ( $element eq "i" ) {
$element = htmlinsert($offset++);
}
}
$element is an alias so if you change it, you change the array. ($_ will do the same, but I dislike using it when I don't have to, because I think it make less clear code. This is a style/choice matter, rather than a technical one).
However for searching and replacing an element in a string – like you're doing – then you're probably better off using one of the other things perl does really well – regular expressions and pattern replacement. I can't give an example easily though, without knowing what htmlinsert returns.
Something like though:
$loop =~ s/i/newvalue/g;
Will replace all instances of i with a new value.

=~ is Perl's "match regular expression" operator, so unless htmlinsert() returns a regex, it's probably not what you meant to do. You probably want to use =.
A more Perlish way to do this, though, might be to use the map function. map takes a block and an array and runs the block with each element of the array in $_, returning all the values returned by that block. For example:
my #wholeloop = map {
$_ eq "i" ? htmlinsert($offset++) : $_;
} split //, $loop;
(The ? and : perform an "if/else" in a single line; they're borrowed from C. map is borrowed from functional programming languages.)

Perhaps you should use foreach. It is the most suitable for what you are trying to do here
my #array;
foreach ( #array ) {
$_ =~ whatever your replacement is;
}
Now, like Sobrique said, unless htmlinsert returns a RegEx value, that isn't going to work. Also, if you could give us context for "$offset", and what its purpose is, that would be really helpful.

Related

Perl: grep from multiple arrays at once

I have multiple arrays (~32). I want to remove all blank elements from them. How can it be done in a short way (may be via one foreach loop or 1-2 command lines)?
I tried the below, but it's not working:
my #refreshArrayList=("#list1", "#list2", "#list3","#list4", "#list5", "#list6" , "#list7");
foreach my $i (#refreshArrayList) {
$i = grep (!/^\s*$/, $i);
}
Let's say, #list1 = ("abc","def","","ghi"); #list2 = ("qwe","","rty","uy", "iop"), and similarly for other arrays. Now, I want to remove all blank elements from all the arrays.
Desired Output shall be: #list1 = ("abc","def","ghi"); #list2 = ("qwe","rty","uy", "iop") ### All blank elements are removed from all the arrays.
How can it be done?
You can create a list of list references and then iterator over these, like
for my $list (\#list1, \#list2, \#list3) {
#$list = grep (!/^\s*$/, #$list);
}
Of course, you could create this list of list references also dynamically, i.e.
my #list_of_lists;
push #list_of_lists, \#list1;
push #list_of_lists, \#list2;
...
for my $list (#list_of_lists) {
#$list = grep (!/^\s*$/, #$list);
}
#$_ = grep /\S/, #$_ for #AoA; # #AoA = (\#ary1, \#ary2, ...)
Explanation
First, this uses the statement modifier, "inverting" the usual for loop syntax into the form STMT for LIST
The for(each) modifier is an iterator: it executes the statement once for each item in the LIST (with $_ aliased to each item in turn).
It is mostly equivalent to a "normal" for loop, with the notable difference being that no scope is set and so there is no need to tear it down either, adding a small measure of efficiency.† Ae can have only one statement; but then again, that can be a do block. Having no scope means that we cannot declare lexical variables for the statement (unless a do block is used).
So the statement is #$_ = grep /\S/, #$_, executed for each element of the list.
In a for(each) loop, the variable that is set to each element in turn as the list is iterated over ("topicalizer") is an alias to those elements. So changing it changes elements. From perlsyn
If VAR is omitted, $_ is set to each value.
If any element of LIST is an lvalue, you can modify it by modifying VAR inside the loop.
In our case $_ is always an array reference, and then the underlying array is rewritten by dereferencing it (#$_) and assigning to that the output list of grep, which consists only of elements that have at least one non-space character (/\S/).
† I ran a three-way benchmark, of the statement-modifier loop against a "normal" loop with and without a topical variable.
For adding 100e6 numbers I get 8-11% speedup (on both a desktop and a server) and with a more involved calculation ($r = ($r + $_ ) / sqrt($_)) it's 4-5%.
A side observation: In both cases the full for loop without a variable (using default $_ for the topicalizer) is 1-2% faster than the one with a lexical topical variable set.

Apply a substitution over every element of an array

I have an array of file names. Names are of the format company_ID_timestamp.
How do I apply a substitution on the array without running a loop?
for ( my $i=0; $i < scalar #todayFiles; $i++ ) {
$todayFiles[$i] = s/_20[0-9]{10}//;
}
Unless you want an ugly hack, you're going to want some kind of a loop, even if it's hidden with a map, or a for statement modifier.
s/_20[0-9]{10}// for #todayFiles;
The following works in Perl v5.14 and up (because of the /r modifier). This one makes sense if you don't want to modify the original array:
my #otherArray = map { s/_20[0-9]{10}//r } #todayFiles;
And here's a shorter/better way to write that C-style loop you showed:
for my $filename (#todayFiles) {
$filename =~ s/_20[0-9]{10}//;
}
The latter one works because the for aka foreach loop actually aliases the variable $filename to the elements of the array being iterated over.
To apply a substitution to every element of an array, there is no way but to iterate over those elements
That said, you can tidy up your code a lot by using for as a statement modifier and employing the $_ default variable
s/_20[0-9]{10}// for #todayFiles;
This still iterates over the entire array, but the code is a lot tighter

How to find index of string in array Perl without iterating

I need to find value in array without iterating through whole array.
I get array of strings from file, and I need to get index of some value in this array, I have tried this code, but it doesn't work.
my #array =<$file>;
my $search = "SomeValue";
my $index = first { $array[$_] eq $search } 0 .. $#array;
print "index of $search = $index\n";
Please suggest how can I get index of value, or it is better to get all indexes of line if there are more than one entry.
Thx in advance.
What does "it doesn't work" mean?
The code you have will work fine, except that an element in the array is going to be "SomeValue\n", not "SomeValue". You can remove the newlines with chomp(#array) or include a newline in your $search string.
Your initial question: "I need to find value in array without iterating through whole array."
You can't. It is impossible to check every element of an array, without checking every element of an array. The very best you can do is stop looking once you've found it - but you indicate in your question multiple matches.
There are various options that will do this for you - like List::Util and grep. But they are still doing a loop, they're just hiding it behind the scenes.
The reason first doesn't work for you, is probably because you need to load it from List::Util first. Alternatively - you forgot to chomp, which means your list includes line feeds, where your search pattern doesn't.
Anyway - in the interests of actually giving something that'll do the job:
while ( my $line = <$file> ) {
chomp ( $line );
#could use regular expression based matching for e.g. substrings.
if ( $line eq $search ) { print "Match on line $.\n"; last; }
}
If you want want every match - omit the last;
Alternatively - you can match with:
if ( $line =~ m/\Q$search\E/ ) {
Which will substring match (Which in turn means the line feeds are irrelevant).
So you can do this instead:
while ( <$file> ) {
print "Match on line $.\n" if m/\Q$search\E/;
}

Beginner problems with perl

I have been tasked with correcting bugs in a chunk of Perl code and had a few questions about it (as I am new to Perl).
My first issue is what does the exclamation mark do?
if (!$superceded{returned->{$field}}) {
$found = 0;
foreach (blahblahblah)
My second problem is what does it mean if you have a variable $supercede and a hash %superceded and you write
(keys %$superceded)
Finally I have read up on these next two but I am still unsure on how they work. Setting a variable equal to shift and how to use "last;".
Thanks for any help and advice.
! is logical negation, which means if $cond is true, !$cond will be false. You could learn more about Truth and Falsehood from perlsyn.
If $hashref is a hash reference, then %$hashref is the hash that that reference referred to. For example,
my %hash = ( key1 => "val1", key2 => "val2" );
my $hashref = \%hash; # create a hash reference
while (my ($key, $val) = each %$hashref) {
# do something
}
Oh, you also could write something like this
my $hash = \%hash;
A little confusion to human readers, but Perl will accept it without any problem. To Perl, $hash and %hash are two completely different variables, and could be totally unrelated.
You could learn more about reference from perlref. And #$arrayref is similar, except in this case $arrayref is an array reference, it could be created by $arrayref = \#array;.
Setting a variable equal to shift
Do you mean something like this:
my $val = shift;
In this case, this means my $val = shift #_;, which will remove the first element of #_ and assign it to $val. See perldoc -f shift for further details.
how to use "last;"
If you want to finish a loop early, you could use last. For example,
foreach my $i (1..100) {
print "$i\n";
}
will print 1 to 100, and this
foreach my $i (1..100) {
print "$i\n";
last if $i == 5;
}
will only print 1 to 5. See perlsyn for further details.

Why does perl not allow me to dereference a member of a hash reference into an array?

Perl terms confuse me and it's not my native language, so bear with me. I'll try to use the right terms, but I'll give an example just to make sure.
So I have a hash reference in the variable $foo. Lets say that $foo->{'bar'}->{'baz'} is an array reference. That is I can get the first member of the array by assigning $foo->{'bar'}->{'baz'}->[0] to a scalar.
when I do this:
foreach (#$foo->{'bar'}->{'baz'})
{
#some code that deals with $_
}
I get the error "Not an ARRAY reference at script.pl line 41"
But when I do this it works:
$myarr = $foo->{'bar'}->{'baz'};
foreach (#$myarr)
{
#some code that deals with $_
}
Is there something I'm not understanding? Is there a way I can get the first example to work? I tried wrapping the expression in parentheses with the # on the outside, but that didn't work. Thanks ahead of time for the help.
It's just a precedence issue.
#$foo->{'bar'}->{'baz'}
means
( ( #{ $foo } )->{'bar'} )->{'baz'}
$foo does not contain an array reference, thus the error. You don't get the precedence issue if you don't omit the optional curlies around the reference expression.
#{ $foo->{'bar'}->{'baz'} }
$myarr = $foo->{'bar'}->{'baz'};
foreach (#$myarr)
{
#some code that deals with $_
}
If you replace your $myarr in for loop with its RHS, it looks like: -
foreach (#{$foo->{'bar'}->{'baz'}})
{
#some code that deals with $_
}
It should look like
foreach (#{$foo->{'bar'}->{'baz'}})
{
#some code that deals with $_
}

Resources