Perl remove spaces from array elements - arrays

I have an array which contains n number of elements. So there might be chances the each element could have spaces in the beginning or at the end. So I want to remove the space in one shot. Here is my code snippet which is working and which is not working (The one which not working is able to trim at the end but not from the front side of the element).
Not Working:
....
use Data::Dumper;
my #a = ("String1", " String2 ", "String3 ");
print Dumper(\#a);
#a = map{ (s/\s*$//)&&$_}#a;
print Dumper(\#a);
...
Working:
...
use Data::Dumper;
my #a = ("String1", " String2 ", "String3 ");
print Dumper(\#a);
my #b = trim_spaces(#a);
print Dumper(\#b);
sub trim_spaces
{
my #strings = #_;
s/\s+//g for #strings;
return #strings;
}
...
No idea whats the difference between these two.
If there is any better please share with me!!

Your "not working example" only removes spaces from one end of the string.
The expression s/^\s+|\s+$//g will remove spaces from both ends.
You can improve your code by using the /r flag to return a modified copy:
#a = map { s/^\s+|\s+$//gr } #a;
or, if you must:
#a = map { s/^\s+|\s+$//g; $_ } #a;

This block has two problems:
{ (s/\s*$//)&& $_ }
The trivial problem is that it's only removing trailing spaces, not leading, which you said you wanted to remove as well.
The more insidious problem is the misleading use of &&. If the regex in s/// doesn't find a match, it returns undef; on the left side of a &&, that means the right side is never executed, and the undef becomes the value of the whole block. Which means any string that the regex doesn't match will be removed and replaced with a undef in the result array returned by map, which is probably not what you want.
That won't actually happen with your regex as written, because every string matches \s*, and s/// still returns true even if it doesn't actually modify the string. But that's dependent on the regex and a bad assumption to make.
More generally, your approach mixes and matches two incompatible methods for modifying data: mutating in place (s///) versus creating a copy with some changes applied (map).
The map function is designed to create a new array whose elements are based in some way on an input array; ideally, it should not modify the original array in the process. But your code does – even if you weren't assigning the result of map back to #a, the s/// modifies the strings inside #a in place. In fact, you could remove the #a = from your code and get the same result. This is not considered good practice; map should be used for its return value, not its side effects.
If you want to modify the elements of an array in place, your for solution is actually the way to go. It makes it clear what you're doing and side effects are OK.
If you want to keep the original array around and make a new one with the changes applied, you should use the /r flag on the substitutions, which causes them to return the resulting string instead of modifying the original in place:
my #b = map { s/^\s+|\s+$//gr } #a;
That leaves #a alone and creates a new array #b with the trimmed strings.

Related

Group similar element of array together to use in foreach at once in perl

i have an array which contents elements in which some elements are similiar under certain conditions (if we detete the "n and p" from the array element then the similiar element can be recognised) . I want to use these similiar element at once while using foreach statement. The array is seen below
my #array = qw(abc_n abc_p gg_n gg_p munday_n_xy munday_p_xy soc_n soc_p);
Order of the array element need not to be in this way always.
i am editing this question again. Sorry if i am not able to deliver the question properly. I have to print a string multiple times in the file with the variable present in the above array . I am just trying to make you understand the question through below code, the below code is not right in any sense .... i m just using it to make you understand my question.
open (FILE, ">" , "test.v");
foreach my $xy (#array){
print FILE "DUF A1 (.pin1($1), .pin2($2));" ; // $1 And $2 is just used to explain that
} // i just want to print abc_n and abc_p in one iteration of foreach loop and followed by other pairs in successive loops respectively
close (FILE);
The result i want to print is as follows:
DUF A1 ( .pin1(abc_n), .pin2(abc_p));
DUF A1 ( .pin1(gg_n), .pin2(gg_p));
DUF A1 ( .pin1(munday_n_xy), .pin2(munday_p_xy));
DUF A1 ( .pin1(soc_n), .pin2(soc_p));
The scripting language used is perl . Your help is really appreciated .
Thank You.!!
Partitioning a data set depends entirely on how data are "similiar under certain conditions."
The condition given is that with removal of _n and _p the "similar" elements become equal (I assume that underscore; the OP says n and p). In such a case one can do
use warnings;
use strict;
use feature 'say';
my #data = qw(abc_n abc_p gg_n gg_p munday_n_xy munday_p_xy soc_n soc_p);
my %part;
for my $elem (#data) {
push #{ $part{ $elem =~ s/_(?:n|p)//r } }, $elem;
}
say "$_ => #{$part{$_}}" for keys %part;
The grouped "similar" strings are printed as a demo since I don't understand the logic of the shown output. Please build your output strings as desired.
If this is it and there'll be no more input to process later in code, nor will there be a need to refer to those common factors, then you may want the groups in an array
my #groups = values %part;
If needed throw in a suitable sorting when writing the array, sort { ... } values %part.
For more fluid and less determined "similarity" try "fuzzy matching;" here is one example.

How do I pass in the name of an array index in perl?

Background
I have these four lines of code that I am trying to extract into one function:
my #firstList = split /\s+/, $doubleArray{A_I}{B_I};
#firstList = shuffle(#firstList);
my #secondList = split /\s+/, $doubleArray{A_I}{C_I};
#secondList = shuffle(#secondList);
Since the only functional difference is the second index of the two dimensional array , I wanted to make it so that the second index ("B_I" and "C_I") are passed into a function.
Proposed solution
In other words I want to make a function like the one below:
my funkyFunc($){
my $index = shift;
my #list = split /\s+/, $doubleArray{A_I}{$index};
#list = shuffle(#list);
return #list;
}
I would intend to use it as such:
my #firstList = funkyFunc("B_I");
my #secondList = funkyFunc("C_I");
However, I'm unsure as to how perl would interpret "B_I" or "C_I", or likewise B_I and C_I. Because of this complexity, I wanted to know...
Question
Is there a way in perl to pass in the name of an array index?
Those aren't arrays, those are hashes (dictionaries, associative arrays in other languages). With that, the B_I and C_I are then treated as bareword strings. If these were actual arrays, then these would be treated as bareword function calls, and you'd have to have those available to be called in the caller, and no need for quoting there.
Since you're using hashes, the keys are strings, and you passing in 'B_I' will result in $index being a string of B_I, and since your {$index} has something that isn't allowed in a bareword string (the $), perl will interpret it as a variable instead of a literal, and everything will work exactly the way you want.

Perl unit testing: check if a string is an array

I have this function that I want to test:
use constant NEXT => 'next';
use constant BACK => 'back';
sub getStringIDs {
return [
NEXT,
BACK
];
}
I've tried to write the following test, but it fails:
subtest 'check if it contains BACK' => sub {
use constant BACK => 'back';
my $strings = $magicObject->getStringIDs();
ok($strings =~ /BACK/);
}
What am I doing wrong?
Your getStringIDs() method returns an array reference.
The regex binding operator (=~) expects a string on its left-hand side. So it converts your array reference to a string. And a stringified array reference will look something like ARRAY(0x1ff4a68). It doesn't give you any of the contents of the array.
You can get from your array reference ($strings) to an array by dereferencing it (#$strings). And you can stringify an array by putting it in double quotes ("#$strings").
So you could do something like this:
ok("#$strings" =~ /BACK/);
But I suspect, you want word boundary markers in there:
ok("#$strings" =~ /\bBACK\b/);
And you might also prefer the like() testing function.
like("#$strings", qr[\bBACK\b], 'Strings array contains BACK');
Update: Another alternative is to use grep to check that one of your array elements is the string "BACK".
# Note: grep in scalar context returns the number of elements
# for which the block evaluated as 'true'. If we don't care how
# many elements are "BACK", we can just check that return value
# for truth with ok(). If we care that it's exactly 1, we should
# use is(..., 1) instead.
ok(grep { $_ eq 'BACK' } #$strings, 'Strings array contains BACK');
Update 2: Hmm... the fact that you're using constants here complicates this. Constants are subroutines and regexes are strings and subroutines aren't interpolated in strings.
The return value of $magicObject->getStringIDs is an array reference, not a string. It looks like the spirit of your test is that you want to check if at least one element in the array pattern matches BACK. The way to do this is to grep through the dereferenced array and check if there are a non-zero number of matches.
ok( grep(/BACK/,#$strings) != 0, 'contains BACK' );
At one time, the smartmatch operator promised to be a solution to this problem ...
ok( $strings ~~ /BACK/ )
but it has fallen into disrepute and should be used with caution (and the no warnings 'experimental::smartmatch' pragma).
The in operator is your friend.
use Test::More;
use syntax 'in';
use constant NEXT => 'next';
use constant BACK => 'back';
ok BACK |in| [NEXT, BACK], 'BACK is in the arrayref';
done_testing;

How to add each element of array to another array's each element?

I have these sets of arrays which has two elements for each.
#a = ("a", "b");
#i = (1, 2);
#s = ( "\\!", "\?");
How do I make the result such that it'll return
a1!, b2?
And I need them to be a new set of an array like
#new =(a1!,b2?)
I wrote the code for output of the answer
$i = length(#a);
for (0..$1) {
#array = push(#array, #a[$i], #s[$i];
}
print #array;
However, it only returned
syntax error at pra.pl line 10, near "];"
Thank you in advance.
use 5.008;
use List::AllUtils qw(zip_by);
⋮
my #new = zip_by { join '', #_ } \#a, \#i, \#s;
zip_by is a subroutine from the List::AllUtils module on CPAN. So it's not built-in.
use v6;
⋮
my #new = map { .join }, zip #a, #i, #s;
In Perl 6, zip is already part of the standard library. This additionaly solution is here for flavour, it's an opportunity to show off strengths: does the same job, but with less syntax in comparison, and works out of the box.
v6 is not strictly necessary, here I just used it for contrast to indicate the version. But at the beginning of a file it also has the nice property that if you accidentally run Perl 6 code in Perl 5, you'll get a nice error message instead of a cryptic syntax error. Try it! From the use VERSION documentation:
An exception is raised if VERSION is greater than the version of the current Perl
The basic idea you have is good, to iterate simultaneously using index of an array. But the code has many elementary errors and it also doesn't do what the examples show. I suggest to first make a thorough pass through a modern and reputable Perl tutorial.
The examples indicate that you want to concatenate (see . operator) elements at each index
use warnings;
use strict;
use feature 'say';
my #a1 = ('a', 'b');
my #a2 = (1, 2);
my #a3 = ('!', '?');
my #res;
foreach my $i (0..$#a1) {
push #res, $a1[$i] . $a2[$i] . $a3[$i];
}
say for #res;
where $#a1 is the index of the last element of array #a1. This assumes that all arrays are of the same size and that all their elements are defined.
This exact work can be done using map in one statement
my #res = map { $a1[$_] . $a2[$_] . $a3[$_] } 0..$#a1;
with the same, serious, assumptions. Even if you knew they held, do you know for sure, in every run on any data? For a robust approach see the answer by mwp.
There is also each_array from List::MoreUtils, providing a "simultaneous iterator" for all arrays
my $ea = each_array(#a1, #a2, #a3);
my #res;
while ( my ($e1, $e2, $e3) = $ea->() ) {
push #res, $e1 . $e2 . $e3
}
which is really useful for more complex processing.
A quick run through basics
Always have use warnings; and use strict; at the beginning of your programs. They will catch many errors that would otherwise take a lot of time and nerves.
Don't use single-letter variable names. We quickly forget what they meant and the code gets hard to follow, and they make it way too easy to make silly mistakes.
Array's size is not given by length. It is normally obtained using context -- when an array is assigned to a scalar the number of its elements is returned. For iteration over indices there is $#ary, the index of the last element of #ary. Then the list of indices is 0 .. $#ary, using the range (..) operator
The sigil ($, #, %) at the beginning of an identifier (variable name) indicates the type of the variable (scalar, array, hash). An array element is a scalar so it needs $ -- $ary[0]
The push doesn't return array elements but it rather adds to the array in its first argument the scalars in the list that follows.
The print #array; prints array elements without anything between them. When you quote it spaces are added, print "#array\n";. Note the handy feature say though, which adds the new line.
Always use strict; and use warnings; (and use my to declare variables).
You don't need to escape ? and ! in Perl strings, and you can use the qw// quote-like operator to easily build lists of terms.
You are using length(#a) to determine the last index, but Perl array indexes are zero-based, so the last index would actually be length(#a) - 1. (But that's still not right. See the next point...)
To get an array's length in Perl, you want to evaluate it in scalar context. The length function is for strings.
You have not accounted for the situation when the arrays are not all the same length.
You assign the last index to a variable $i, but then you reference the variable $1 on the next line. Those are two different variables.
You are iterating from zero to the last index, but you aren't explicitly assigning the iterator to a variable, and you aren't using the implicit iterator variable ($_).
To get a single array element by index in Perl, the syntax is $a[$i], not #a[$i]. Because you only want a single, scalar value, the expression has to start with the scalar sigil $. (If instead you wanted a list of values from an expression, you would start the expression with the array sigil #.)
push modifies the array given by the first argument, so there's no need to assign the result back to the array in the expression.
You are missing a closing parenthesis in your push expression.
In your same code, you have #new and #array, and you are only adding elements from #a and #s (i.e. you forgot about #i).
You are pushing elements onto the array, but you aren't concatenating them into the desired string format.
Here is a working version of your implementation:
use strict;
use warnings;
use List::Util qw{max};
my #a = ("a", "b");
my #i = ("1", "2");
my #s = ("!", "?");
my #array;
my $length = max scalar #a, scalar #i, scalar #s;
foreach my $i (0 .. $length - 1) {
push #array, ($a[$i] // '') . ($i[$i] // '') . ($s[$i] // '');
}
print #array;
(The // means "defined-or".)
Here's how I might write it:
use strict;
use warnings;
use List::Util qw{max};
my #a = qw/a b/;
my #i = qw/1 2/;
my #s = qw/! ?/;
my #array = map {
join '', grep defined, $a[$_], $i[$_], $s[$_]
} 0 .. max $#a, $#i, $#s;
print "#array\n";
(The $#a means "give me the index of the last element of the array #a.")
use warnings;
use strict;
use Data::Dumper;
my $result = [];
my #a = ("a", "b");
my #i = (1, 2);
my #s = ( "\!", "\?");
my $index = 0;
for my $a ( #a ) {
push( #$result, ($a[$index], $i[$index], $s[$index]) );
$index = $index + 1;
}
print Dumper(#$result);

How to copy data to an array in perl?

I am trying access the data from the database and copy the data to an array. This is my code,
$sth = $dbh->prepare("SELECT * FROM abcd WHERE id=100 ");
$sth->execute;
$N=$sth->rows;
print "$N\n";
while (my #row_val = $sth->fetchrow_array()){
my ($uniqid, $time, $current, $id ) = #row_val;
$y[k]=$current;
$k++;
}
for ($k=0;$k<$N;$k++) {
print "$y[k]\t";
}
But it displays the same value for all $y[k]. How to copy the data from database to an array in perl?
You are using a bareword here:
$y[k]=$current;
# ^--- here, the "k" is a bareword
If you use warnings this will give a warning
Unquoted string "k" may clash with future reserved word at foo.pl line 10.
Argument "k" isn't numeric in array element at foo.pl line 10.
And the "k" will be interpreted as a string, will be converted to a number, which will be zero 0, so all your data is stored in $y[0].
This is why it is a very bad idea to not turn warnings on.
What you probably want instead is to push the new values onto the array:
push #y, $current;
This is, IMO, preferable to using an index, since it does all that work for you. Usually, you only want to specifically get involved with array indexes if the indexes themselves are of value for you, such as when comparing array elements.
This also means that your subsequent for loop
for ($k=0;$k<$N;$k++) {
print "$y[k]\t";
}
Is better written
for (#y) {
print "$_\t";
}
Although this is better written with join:
print join "\t", #y;
As a final note, you should always use
use strict;
use warnings;
It takes a small amount of learning to overcome the additional noise when using these pragmas, but it is well worth it in terms of learning and reducing your time spent debugging. I usually say that not using these pragmas is like covering up the low oil warning lamp in your car: Not knowing about the errors does not solve them.
This behaviour is because you are putting everything to index "k" - not any number just "k",
it is only a coincidence that its working at all :) - the "same value" is the last value - isnt it ? :)
SOLUTION:
1) variables are written with $ - keep that in mind when accessing $yourArray[$variableWithIndex]
2) $y[k]=$current; # wrong! you are trying to access "k" index
correct: $y[$k]=$current;
Didnt tested it - but this should work:
$sth = $dbh->prepare("SELECT * FROM abcd WHERE id=100 ");
$sth->execute;
$N=$sth->rows;
print "$N\n";
$k=0; # init first!
while (my #row_val = $sth->fetchrow_array()){
my ($uniqid, $time, $current, $id ) = #row_val;
$y[$k]=$current; # dont forget the $
$k++;
}
for ($k=0;$k<$N;$k++) {
print "$y[$k]\t"; # dont forget the $
}

Resources