Add quotes to all elements inside of an array in perl - arrays

I'm trying to follow this tutorial for my own code which basically right now reads a value into a scalar which is pushed into an array called states. However, it doesnt properly hash the function like in the tutorial and I believe its because the contents of the array isn't properly quoted.
I've tried
foreach (#states)
{
q($_);
}
and
push #states, q($key);
but neither produces the necessary output. Currently my output displays as
NY, NJ, MI , NJ
when using
print join(", ", #states);
I want it to display
'NY', 'NJ', 'MI' , 'NJ'

Take states, map them to quoted strings, join by comma:
my #states = qw( NY NJ MI );
print join ', ', map "'$_'", #states;

To add quotes around a value you can use double-quoted string interpolation:
"'$_'"
Or you can use string concatenation:
"'".$_."'"
So you can write your foreach loop as follows:
foreach (#states) {
$_ = "'$_'";
}
Note that $_ must be assigned, otherwise the body of the loop has no effect (this is the case with your q($_); code).
Full demo:
use strict;
use warnings;
my #states = qw(NY NJ MI NJ);
foreach (#states) {
$_ = "'$_'";
}
print(join(', ', #states ));
'NY', 'NJ', 'MI', 'NJ'

One other way:
use strict;
use warnings;
my #states = qw/ NY NJ MI NJ /;
my $output = join ', ', map qq/'$_'/, #states;
print $output;
Would result to a formatted list (string) of single quoted elements, each separated as you expect.
'NY', 'NJ', 'MI', 'NJ'

Related

compare an array of string with another array of strings in perl

I want to compare an array of string with another array of strings; if it matches, print matched.
Example:
#array = ("R-ID 1.0001", "RA-ID 61.02154", "TCA-ID 49.021456","RCID 61.02154","RB-ID 61.02154");
#var = ("TCA-ID 49", "R-ID 1");
for (my $x = 0; $x <= 4; $x++)
{
$array[$x] =~ /(.+?)\./;
if( ($var[0] eq $1) or ($var[1] eq $1) )
{
print "\n deleted rows are :#array\n";
}
else
{
print "printed rows are : #array \n";
push(#Matrix, \#array);
}
Then I need to compare #var with the #array; if it is matched, print the matched pattern.
Here the entire logic is in a hireartical for loop which gives a new #array in each iteration. so every time this logic is executed #array has different strings.
Then comes with #var it is user input field, this #var can be of any size. So in order to run the logic according to these constraints, I need to iterate the condition inside the if loop when the user input #var size is 3 for example.
So the goal is to match and delete the user input stings using the above mentioned logic. But unfortunately tis logic is not working. Could you please help me out in this issue.
The builtin grep keyword is a good place to start.
my $count = grep { $_ eq $var } #array;
This returns a count of items ($_) in the array which are equal (eq) to $var.
If you needed case-insensitive matching, you could use lc (or in Perl 5.16 or above, fc) to do that:
my $count = grep { lc($_) eq lc($var) } #array;
Now, a disadvantage to grep is that it is counting the matches. So after if finds the first match, it will keep on going until the end of the array. You don't seem to want that, but just want to know if any item in the array matches, in which case keeping on going might be slower than you need if it's a big array with thousands of elements.
So instead, use any from the List::Util module (which is bundled with Perl).
use List::Util qw( any );
my $matched = any { $_ eq $var } #array;
This will match as soon as it finds the first matching element, and skip searching the rest of the array.
Here is a couple of versions that allows multiple strings to be matched. Not clear what form $var takes when you want to store multiple, so assuming they are in an array #var for now.
The key point is this one is the use of the lookup hash to to the matching.
use strict;
use warnings;
my #var = ("TCA-ID 49", "RA-ID 61");
my #array = ("R-ID 1", "RA-ID 61", "TCA-ID 49");
# create a lookup for the strings to match
my %lookup = map { $_ => 1} #var ;
for my $entry (#array)
{
print "$entry\n"
if $lookup{$entry} ;
}
running gives
RA-ID 61
TCA-ID 49
Next, using a regular expression to do the matching
use strict;
use warnings;
my #var = ("TCA-ID 49", "RA-ID 61");
my #array = ("R-ID 1", "RA-ID 61", "TCA-ID 49");
my $re = join "|", map { quotemeta } #var;
print "$_\n" for grep { /^($re)$/ } #array ;
output is the same

Perl: Removing array items and resizing the array

I’m trying to filter an array of terms using another array in Perl. I have Perl 5.18.2 on OS X, though the behavior is the same if I use 5.010. Here’s my basic setup:
#!/usr/bin/perl
#use strict;
my #terms = ('alpha','beta test','gamma','delta quadrant','epsilon',
'zeta','eta','theta chi','one iota','kappa');
my #filters = ('beta','gamma','epsilon','iota');
foreach $filter (#filters) {
for my $ind (0 .. $#terms) {
if (grep { /$filter/ } $terms[$ind]) {
splice #terms,$ind,1;
}
}
}
This works to pull out the lines that match the various search terms, but the array length doesn’t change. If I write out the resulting #terms array, I get:
[alpha]
[delta quadrant]
[zeta]
[eta]
[theta chi]
[kappa]
[]
[]
[]
[]
As you might expect from that, printing scalar(#terms) gets a result of 10.
What I want is a resulting array of length 6, without the four blank items at the end. How do I get that result? And why isn’t the array shrinking, given that the perldoc page about splice says, “The array grows or shrinks as necessary.”?
(I’m not very fluent in Perl, so if you’re thinking “Why don’t you just...?”, it’s almost certainly because I don’t know about it or didn’t understand it when I heard about it.)
You can always regenerate the array minus things you don't want. grep acts as a filter allowing you to decide which elements you want and which you don't:
#!/usr/bin/perl
use strict;
my #terms = ('alpha','beta test','gamma','delta quadrant','epsilon',
'zeta','eta','theta chi','one iota','kappa');
my #filters = ('beta','gamma','epsilon','iota');
my %filter_exclusion = map { $_ => 1 } #filters;
my #filtered = grep { !$filter_exclusion{$_} } #terms;
print join(',', #filtered) . "\n";
It's pretty easy if you have a simple structure like %filter_exclusion on hand.
Update: If you want to allow arbitrary substring matches:
my $filter_exclusion = join '|', map quotemeta, #filters;
my #filtered = grep { !/$filter_exclusion/ } #terms;
To see what's going on, print the contents of the array in each step: When you splice the array, it shrinks, but your loop iterates over 0 .. $#terms, so at the end of the loop, $ind will point behind the end of the array. When you use grep { ... } $array[ $too_large ], Perl needs to alias the non-existent element to $_ inside the grep block, so it creates an undef element in the array.
#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
my #terms = ('alpha', 'beta test', 'gamma', 'delta quadrant', 'epsilon',
'zeta', 'eta', 'theta chi', 'one iota', 'kappa');
my #filters = qw( beta gamma epsilon iota );
for my $filter (#filters) {
say $filter;
for my $ind (0 .. $#terms) {
if (grep { do {
no warnings 'uninitialized';
/$filter/
} } $terms[$ind]
) {
splice #terms, $ind, 1;
}
say "\t$ind\t", join ' ', map $_ || '-', #terms;
}
}
If you used $terms[$ind] =~ /$filter/ instead of grep, you'd still get uninitialized warnings, but as there's no need to alias the element, it won't be created.

Related to regular expressions in Perl

I have a string written in some text file -
A[B[C[10]]]
I need to extract the information of arrays used in this string. Example, I have to store the information in an array,
#str = (A[], B[], C[10])
I want to accomplish this thing using regex in Perl.
I want solution that works for the every case of array inside array, like this
A[B[C[D[E[F[10]]]]]]
So, how to create that #str array?
You can use a regex pattern to parse this string recursively, and call Perl code to save intermediate values
Like this
use strict;
use warnings 'all';
use v5.14;
my #items;
my $i;
my $re = qr{
([A-Z]\[) (?{ $items[$i++] = $^N })
(?:
(?R)
|
(\d+) (?{ $items[$i-1] .= $^N })
)
\] (?{ $items[--$i] .= ']' })
}x;
my $s = 'A[B[C[D[E[F[10]]]]]]';
use Data::Dump;
(#items, $i) = ();
dd \#items if 'A[B[C[10]]]' =~ /$re/g;
(#items, $i) = ();
dd \#items if 'A[B[C[D[E[F[10]]]]]]' =~ /$re/g;
output
["A[]", "B[]", "C[10]"]
["A[]", "B[]", "C[]", "D[]", "E[]", "F[10]"]

"Use of uninitialized value" when indexing an array

I get the following error from Perl when trying to run the code below
Use of uninitialized value within #words in concatenation (.) or string...
It references the line where I try to create an array made up of three-word sequences (the line that starts with $trigrams). Can anyone help me figure out the problem?
my %hash;
my #words;
my $word;
my #trigrams;
my $i = 0;
while (<>) {
#words = split;
foreach $word (#words) {
$hash{$word}++;
# Now trying to create the distinct six-grams in the 10-K.
$trigrams[$i] = join " ", $words[$i], $words[$i + 1], $words[$i + 2];
print "$words[$i]\n";
$i++;
}
}
All that is happening is that you are falling off the end of the array #words. You are executing the loop for each element of #words, so the value of $i goes from 0 to $#words, or the index of the final element of the array. So the line
join " ", $words[$i], $words[$i + 1], $words[$i + 2];
accesses the last element of the array $words[$i] and two elements beyond that which don't exist.
In this case, as with any loop which uses the current index of an array, it is easiest to iterate over the array indices instead of the contents. For the join to be valid you need to start at zero and stop at two elements before the end, so 0 .. $#words-2.
It is also neater to use an array slice to select the three elements for the trigram, and use the fact that interpolating an array into a string, as in "#array", will do the same as join ' ', #array. (More precisely, it does join $", #array, and $" is set to a single space by default.)
I suggest this fix. It is essential to use strict and use warnings at the start of every Perl program, and you should declare all your variables using my as late as possible.
use strict;
use warnings;
my %hash;
while (<>) {
my #words = split;
my #trigrams;
for my $i (0 .. $#words - 2) {
my $word = $words[$i];
++$hash{$word};
$trigrams[$i] = "#words[$i,$i+1,$i+2]";
print "$word\n";
}
}
Update
You may prefer this if it isn't too terse for you
use strict;
use warnings;
my %hash;
while (<>) {
my #words = split;
my #trigrams = map "#words[$_,$_+1,$_+2]", 0 .. $#words-2;
}

Check words and synonyms

I have an array with some words, and another array with words and synonyms. I'd like to create a third array when I find a matchin word between first and second array. I tried with grep but I'm not able to write the code in a proper way in order to get what I want.
The problem is that elements in array 1 can be found in array 2 at the beginning but also at the end or in the middle.
Maybe it's easier with an exemple:
#array1 = qw(chose, abstraction);
#array2 = (
"inspirer respirer",
"incapable",
"abstraction",
"abaxial",
"cause,chose,objet",
"ventral",
"chose,objet"
);
The result it should be
#array3 = ("abstraction", "cause,chose,objet", "chose,objet");
Is it right to use "grep"?
I'm not able to write a right syntax to solve the problem..
Thank you
You can construct a regular expression from the array1, then filter the array2 using it:
#!/usr/bin/perl
use warnings;
use strict;
my #array1 = qw(chose, abstraction);
my #array2 = (
"inspirer respirer",
"incapable",
"abstraction",
"abaxial",
"cause,chose,objet",
"ventral",
"chose,objet"
);
my $regex = join '|', map quotemeta $_, #array1; # quotemeta needed for special characters.
$regex = qr/$regex/;
my #array3 = grep /$regex/, #array2;
print "$_\n" for #array3;
I know you have an answer but here is a fun way I thought of.
So, I guess it is like an inverted index.
You take each set of synonyms and make them into an array. Then take each element of that array and put it into a hash as the keys with the value being a reference to the array.
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my #array1 = qw(chose abstraction);
my #array2 = ("inspirer respirer",
"incapable",
"abstraction",
"abaxial",
"cause,chose,objet",
"ventral",
"chose,objet"
);
my #array;
push #array, map { /,|\s/ ? [split(/,|\s/, $_)]:[$_] } #array2;
my %construct;
while(my $array_ref = shift(#array)){
for(#{ $array_ref }){
push #{ $construct{$_} }, $array_ref;
}
}
my #array3 = map { s/,//; (#{ $construct{$_} }) } #array1;
print join(', ', #{ $_ }), "\n" for (#array3);
EDIT:
Missed apart of the answer before, this one should be complete.

Resources