Error while using ternary operator in perl - perl

I am trying to print an array with the following format:
1 .. double quote each element
2 .. put a comma between elements
for some reason my code below doesn't generate the correct format
my #new_arr = (0 .. 5);
my $new_str = "";
for my $index ( 0 .. $#new_arr)
{
print "$index .. $new_arr[$index]\n";
($index == $#new_arr) ? $new_str.= "+\"$new_arr[$index]\"":$new_str.= "\"$new_arr[$index]\"\,";
}
print "$new_str \n";

$new_str = q{"} . join( q{","}, #new_arr ) . q{"};
Example:
#new_arr = (0..5);
$new_str = q{"} . join( q{","}, #new_arr ) . q{"};
print $new_str,"\n";
Output:
"0","1","2","3","4","5"
perl -MO=Deparse,-p ... demonstrates the problem with your original code:
$ perl -MO=Deparse,-p ternary.pl
(#a = (0..5));
foreach my($index) (0 .. $#a) {
print("$index .. $new_arr[$index]\n");
((($index == $#a) ? ($new_str .= qq[+"$a[$index]"]) : $new_str) .= qq["$a[$index]",]);
}
print("$new_str \n");
Namely, that the .= operators in the ternary expression are not grouped correctly. You could salvage it by using parentheses appropriately:
($index == $#new_arr) ? ($new_str.= "+\"$new_arr[$index]\""):($new_str.= "\"$new_arr[$index]\"\,");
or pulling $new_str .= out of each side of the ternary expression
$new_str .= $index == $#new_arr ? "+\"$new_arr[$index]\"" : "\"$new_arr[$index]\"\,";
but ultimately this is a good use case for Perl's builtin join function.

$ perl -wE 'say join(",", map { qq{"$_"} } 0 .. 5)'
Output:
"0","1","2","3","4","5"
The initial map adds the double quotes, and then join creates a comma-separated string from the newly double-quoted list elements. We need to use qq here because we want an interpolated string containing double quotes.

Related

Splitting and tallying substrings within mixed integer-string data

Input Data (example):
40A3B35A3C
30A5B28A2C2B
Desired output (per-line) is a single number determined by the composition of the code 40A3B35A3C and the following rules:
if A - add the proceeding number to the running total
if B - add the proceeding number to the running total
if C - subtract the proceeding number from the running total
40A 3B 35A 3C would thus produce 40 + 3 + 35 - 3 = 75.
Output from both lines:
75
63
Is there an efficient way to achieve this for a particular column (such as $F[2]) in a tab-delimited .txt file using a one-liner? I have considered splitting the entire code into individual characters, then performing if statement checks to detect A/B/C, but my Perl knowledge is limited and I am unsure how to go about this.
When you use split with a capture, the captured group is returned from split, too.
perl -lane '
#ar = split /([ABC])/, $F[2];
$s = 0;
$s += $n * ("C" eq $op ? -1 : 1) while ($n, $op) = splice #ar, 0, 2;
print $s
' < input
Or maybe more declarative:
BEGIN { %one = ( A => 1,
B => 1,
C => -1 ) }
#ar = split /([ABC])/, $F[2];
$s = 0;
$s += $n * $one{$op} while ($n, $op) = splice #ar, 0, 2;
print $s
When working through a string like this, it's useful to know that regular expressions can return a list of results.
E.g.
my #matches = $str =~ m/(\d+[A-C])/g; #will catch repeated instances
So you can do something like this:
#!/usr/bin/env perl
use strict;
use warnings;
while (<DATA>) {
my $total;
#break the string into digit+letter groups.
for (m/(\d+[A-C])/g) {
#separate out this group into num and code.
my ( $num, $code ) = m/(\d+)([A-C])/;
print "\t",$num, " => ", $code, "\n";
if ( $code eq "C" ) {
$total -= $num;
}
else {
$total += $num;
}
}
print $total, " => ", $_;
}
__DATA__
40A3B35A3C
30A5B28A2C2B
perl -lne 'push #a,/([\d]+)[AB]/g;
push #b,/([\d]+)[C]/g;
$sum+=$_ for(#a);$sum-=$_ for(#b);
print $sum;#a=#b=();undef $sum' Your_file
how it works
use the command line arg as the input
set the hash "%op" to the
operations per letter
substitute the letters for operators in the
input evaluate the substituted input as an expression
use strict;
use warnings;
my %op=qw(A + B + C -);
$ARGV[0] =~ s/(\d+)(A|B|C)/$op{$2} $1/g;
print eval($ARGV[0]);

Cannot split line and save to variable using whitespace in perl

I'm having some trouble with parsing a file.
Two lines in the file contain the word ' Mapped', and I would like to extract the number that is in those two lines.
And this is my code:
my %cellHash = ();
my $mapped = 0;
my $alnPairs = 0;
my #mappedReads = ();
while (<ALIGN_SUMMARY>) {
chomp($_);
if (/Mapped/) {
print "\n$_\n";
$mapped = (split / /, $_)[2];
push(#mappedReads, $mapped);
}
if (/Aligned pairs/) {
print "\n$_\n";
$alnPairs = (split / /, $_)[4];
}
}
{ $cellHash{$cellDir} } = (
'MappedR1' => $mappedReads[0] ,
'MappedR2' => $mappedReads[1] ,
'AlnPairs' => $alnPairs ,
);
foreach my $cellName ( keys %cellHash){
print OUTPUT $cellName,
"\t", ${ $cellHash{$cellName} }{"LibSize"},
"\t", ${ $cellHash{$cellName} }{"MappedR1"},
"\t", ${ $cellHash{$cellName} }{"MappedR2"},
"\t", ${ $cellHash{$cellName} }{"AlnPairs"},
"\n";
}
But the OUTPUT file only has the 'AlignedPairs' column and never anything in MappedR1 or MappedR2.
What am I doing wrong? Thanks!
When I look at the file, it looks like there is more than a single space. Here is an example of what I mean and what I did to extract the number.
my $test = "blah : 123455";
my #test_ary = split(/ /, $test);
print scalar #test_ary . "\n"; # Prints the size of the array
$number = $1 if $test =~ m/([0-9]+)/;
print "$number\n"; # Prints the extracted number
Output of run:
Size of array: 8
The extracted number: 123455
Hope this helps.
First off, paste in your actual input and output if you want anyone to actually test somethnig for you, not an image.
Second, you're not splitting on whitespace, you're splitting on a single literal space. Use the special case of
split ' ', $_;
to split on arbitrary length whitespace, discarding leading and trailing whitespace.

extracting numbers from strings in perl

$string1 = "2_0_1AU13682.0AV+0.2"
$string2 = "2_0_1AT+0.1CD13681.9"
I have these 2 strings. How do I extract the 7 char decimal number from both of them?
From string 1 that would be 13682.0 and from string 2 that would be 13681.9.
The decimal number is always 7 characters and always in the form of xxxxx.x
Try doing this using regex and match operator :
my $string1 = "2_0_1AU13682.0AV+0.2";
my ($res) = $string1 =~ m/(\d{5}\.\d)/;
print $res, "\n";
See Extracting-matches and Regexp-Quote-Like-Operators.
Use a regular expression, like this:
my #data = qw( 2_0_1AU13682.0AV+0.2 2_0_1AT+0.1CD13681.9 );
foreach my $str (#data) {
if ($str =~ /(\d{5}\.\d)/) {
print $1, "\n";
}
}
Outputs:
13682.0
13681.9

Adding an "and" before the last element in a comma-interpolated array in Perl

I want to create a subroutine that adds commas to elements and adds an "and" before the last element, e.g., so that "12345" becomes "1, 2, 3, 4, and 5". I know how to add the commas, but the problem is the result I get is "1, 2, 3, 4, and 5," and I don't know how to get rid of the last comma.
sub commas {
my #with_commas;
foreach (#_) {
push (#with_commas, ($_, ", ")); #up to here it's fine
}
splice #with_commas, -2, 1, ("and ", $_[-1]);
#with_commas;
}
As you can probably tell, I'm trying to delete the last element in the new array (#with_commas), since it has the comma appended, and add in the last element in the old array (#_, passed to the sub routine from the main program, with no added comma).
When I run this, the result is, e.g., "1, 2, 3, 4, and 5," -- with the comma at the end. Where is that comma coming from? Only #with_commas was supposed to get the commas.
Any help is appreciated.
sub format_list {
return "" if !#_;
my $last = pop(#_);
return $last if !#_;
return join(', ', #_) . " and " . $last;
}
print format_list(#list), "\n";
This also handles lists with only one element, unlike most of the other answers.
You could use join and modify the last element to include an and:
my #list = 1 .. 5;
$list[-1] = "and $list[-1]" if $#list;
print join ', ', #list;
There is a CPAN module for this, Lingua::Conjunction. I use it myself, and recommend it over rolling your own solution. The usage syntax is very simple:
conjunction(#list);
#!/usr/bin/perl
use warnings;
use strict;
sub commas {
return "" if #_ == 0;
return $_[0] if #_ == 1;
my $last = pop #_;
my $rest = join (", ", #_);
return $rest.", and ".$last;
}
my #a = (1,2,3,4,5);
print commas(#a), "\n";
Add the commas then add the "and ":
use v5.10;
my $string = join ', ', 1 .. 5;
substr
$string,
rindex( $string, ', ' ) + 2,
0,
'and '
;
say $string;
So, work that in as the case when you have more than two elements:
use v5.10;
my #array = 1..5;
my $string = do {
if( #array == 1 ) {
#array[0];
}
elsif( #array == 2 ) {
join ' and ', #array
}
elsif( #array > 2 ) {
my $string = join ', ', #array;
my $commas = $string =~ tr/,//;
substr
$string,
rindex( $string, ', ' ) + 2,
0,
'and '
;
$string;
}
};
say $string;
Just in the spirit of TIMTOWTDI (though, frankly, #perreal's answer is better as far as readability):
sub commas {
my $last_index = $#_;
my #with_commas = map { (($_==$last_index) ? "and " : "") . $_[$_] }
0 .. $last_index;
print join("," #with_commas)
}
This is somewhat similar to Alan's answer (more convoluted/complicated), but the benefit compared to that is that it would work if you need to add "and " to any OTHER element than the last one; Alan's only works when you know the exact offset (e.g. last element)
Small hint
for( 1 .. 10 ) {
print ;
$_ == 10 ? print '' : ($_ != 9 ? print ', ' : print ' and ');
}

How to pass the xpath that is stored in variable?

my $V1 = $sel->get_text("//body[\#id='ext-gen3']/div[14]/div[2]/div/div/div/div/div/div/div/div/div/div[2]/div/div/table/tbody/tr/td/div/div");
print($V1 . "\n");
When i execute the above command, i could print the v1 without error.
My aim is to print the such values ( say around 20). But xpath for each variable varies by the div element only (i.e., div element preeceeding table should be replaced by div[2], and div[2] should be replaced by div[3] in every iteration.
Following snippet does the replacing div[] element. But saying as invalid xpath.
my $xpath = q(//body[\#id='ext-gen3']/div[14]/div[2]/div/div/div/div/div/div/div/div/div/div[2]/div/div/table/tbody/tr/td/div/div);
for my $i (0,2)
{
my #nodes = split qr'/', $xpath;
$nodes[16] .= "[$i]" unless 0 == $i;
#say join '/', #nodes; }
my #somenames = join('/',#nodes);
#print "\n";
#print #somenames ;
foreach my $val (#somenames) {
chomp($val);
#my #new_arr = $sel->get_text($val);
print "\n";
print $val; ***this prints the xpath value which i needed***
my $V1 = $sel->get_text("$val"); ***#i'm passing the same xpath here within double quotes but says as invalid xpath***
print($V1 . "\n");
my #new_arr = $sel->get_text("$val");
}
}
Am i following wrong way of passing xpath ? How can i do it ?
Compare your definition of the XPath string with mine in the previous answer. You inserted a backslash in front of the commercial-at that does not belong.