How to pass the xpath that is stored in variable? - perl

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.

Related

How to print the count of passes and failures as sequence of 1s and -1s

I wrote this script in perl, where I extract the pass count and failure count and print it as 1 and -1.
Below is what I have tried and it works fairly well but I want to know if there is a way that I can improvise the code?
my $pc = 10;
my $fc = 5;
my $v = "";
my $c = 0;
while ($c < $pc){
if ($c > 0) {
$v .= ",";
}
$c++;
$v .= "1";
}
$v .= ",";
$c=0;
while ($c < $fc){
if ($c > 0) {
$v .= ",";
}
$c++;
$v .= "-1";
}
print $v;
print "\n";
output:1,1,1,1,1,1,1,1,1,1,-1,-1,-1,-1,-1
Appreciate any ideas or suggestions.
Perl offers a wide-range of higher-level tools, and often we don't need explicit loops.
A direct answer to the question
my $v = join ',', (1) x $pc, (-1) x $fc;
We build the list of 1s using the repetition operator (x) on the list (1). Since all that follows the pattern in join is its input list, we can also supply the list of -1s, using the comma operator between them. Elements of this combined (flattened) list are then joined by ,
If you need to resolve dynamically anything about those 1s there'll be more work to do.
Here is another approach, which is suitable for more general conditions.
The part with +1
my $v = join ',', map { 1 } 1..$pc;
and the part with -1
$v .= join ',', map { -1 } 1..$fc;
This can also be made into one line like the first example, if desired and suitable.
The map runs the code in the block for each element of the input list, and returns the resulting list. Inside the block each element is available in the $_ variable and the code normally uses it. This is how we can generate a list from another, in a functional manner.
But in this example you only want the list of 1s, so 1 is returned from the block each time, $pc times. Then that output list becomes input to join and elements are joined by ,. The second line does this $fc times for -1, then appending the joined string to $v.
I have used an array for storing the output which is a more structured way to handle multiple values.Please Find Below the code I have modified.
my $pc = 10;
my $fc = 5;
my #v;
my $c = 0;
$c=$pc;
while ($c){
push #v, "1";
$c--;
}
$c=$fc;
while ($c){
push #v, "-1";
$c--;
}
local $, = ',';
print #v;
print "\n";
Note - Please note that the output is in an array and the separator (,) is just used for formatting the output and is not present in the array. Using $, = ',' we can set any thing as an output separator.
First, let's fix the bug where $pc == 0 and $fc > 0 returns ,-1,-1,... instead of -1,-1,....
my $v = "";
my $c = 0;
for (1..$pc) {
$v .= "," if $c++ > 0;
$v .= "1";
}
for (1..$fc) {
$v .= "," if $c++ > 0;
$v .= "-1";
}
Second, let's use an array to collect the values, and join the contents in order to avoid all that comma code.
my #v;
for (1..$pc) {
push #v, "1";
}
for (1..$fc) {
push #v, "-1";
}
my $v = join(',', #v);
That looks an awful lot like the definition of map[1]! So,
my #v;
push #v, map { "1" } 1..$pc;
push #v, map { "-1" } 1..$fc;
my $v = join(',', #v);
Or just
my $v = join ',',
map({ "1" } 1..$pc),
map({ "-1" } 1..$fc);
Finally, since the body of the map is constant, we can use the repetition operator.
my $v = join ',',
( "1" ) x $pc,
( "-1" ) x $fc;
Which is to say
my #v; for (LIST) { push #v, f($_) }
can be written as
my #v = map { f($_) } LIST;

Error while using ternary operator in 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.

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.

how to replace a div[ ] element with another div[ ] element?

for ($i=0; $i<10; $i++)
{
my $v1 = $sel->get_text("//body[\#id='ext-gen3']/div[10]/div[2]/div/div/div/div/div/div/div/div/div/div[2]/div/**div**/table/tbody/tr/td/div/div");
my $v2 = $sel->get_text("//body[#\id='ext-gen3']/div[10]/div[2]/div/div/div/div/div/div/div/div/div/div[2]/div/**div**/table/tbody/tr/td[2]/div/div")
print ($v1 . $v2);
}
For every iteration, it has to find the 14th element starting from div[10] & replace it with the increased div[ ] element (Ex: if 14th element is div, replace it by div[2]. In the next iterartion find 14th element i.e., div[2] & replace it by div[3] & so on ).
By using PATTERN matching, it can't. Is there any method by using regex for finding that particular element & replacing it ? how can i do it ?
my $a = "//body[\#id='ext-gen3']/div[10]/div[2]/div/div/div/div/div/div/div/div/div/div[2]/div/**div**/table/tbody/tr/td/div/div";
my #arr = split ('/' , $a);
print "#arr \n";
my $size1 = #arr;
print "$size1\n";
print $arr[16];
foreach my $a2 (#arr)
{
print "$a2 \n";
}
my $b = "//body[\#id='ext-gen3']/div[10]/div[2]/div/div/div/div/div/div/div/div/div/div[2]/div/**div**/table/tbody/tr/td[2]/div/div";
Two variables as mentioned in the above question as v1 & v2 (edited as $a and $b), the modification has to apply for both of them. I think i'm almost near to what you've told. Can yoy please help me further
use 5.010;
my $xpath = q(//body[#id='ext-gen3']/div[10]/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..10) {
my #nodes = split qr'/', $xpath;
$nodes[16] .= "[$i]" unless 0 == $i;
say join '/', #nodes;
}
Results:
//body[#id='ext-gen3']/div[10]/div[2]/div/div/div/div/div/div/div/div/div/div[2]/div/div/table/tbody/tr/td/div/div
//body[#id='ext-gen3']/div[10]/div[2]/div/div/div/div/div/div/div/div/div/div[2]/div/div[1]/table/tbody/tr/td/div/div
//body[#id='ext-gen3']/div[10]/div[2]/div/div/div/div/div/div/div/div/div/div[2]/div/div[2]/table/tbody/tr/td/div/div
//body[#id='ext-gen3']/div[10]/div[2]/div/div/div/div/div/div/div/div/div/div[2]/div/div[3]/table/tbody/tr/td/div/div
//body[#id='ext-gen3']/div[10]/div[2]/div/div/div/div/div/div/div/div/div/div[2]/div/div[4]/table/tbody/tr/td/div/div
//body[#id='ext-gen3']/div[10]/div[2]/div/div/div/div/div/div/div/div/div/div[2]/div/div[5]/table/tbody/tr/td/div/div
//body[#id='ext-gen3']/div[10]/div[2]/div/div/div/div/div/div/div/div/div/div[2]/div/div[6]/table/tbody/tr/td/div/div
//body[#id='ext-gen3']/div[10]/div[2]/div/div/div/div/div/div/div/div/div/div[2]/div/div[7]/table/tbody/tr/td/div/div
//body[#id='ext-gen3']/div[10]/div[2]/div/div/div/div/div/div/div/div/div/div[2]/div/div[8]/table/tbody/tr/td/div/div
//body[#id='ext-gen3']/div[10]/div[2]/div/div/div/div/div/div/div/div/div/div[2]/div/div[9]/table/tbody/tr/td/div/div
//body[#id='ext-gen3']/div[10]/div[2]/div/div/div/div/div/div/div/div/div/div[2]/div/div[10]/table/tbody/tr/td/div/div
Ummm, all elements are separated by /, right? So you can use the native split method to split the portion of the text following div[10] based on /. Store it in an array $arr. Merge it to find the length of the string, say $len. Find the index of the div[10], say $orig_index. Then you find the 14th element, do a regex match to see which format it is in:
$arr[13] =~ /div([\d+])?/;
if ($1) {
$arr[13] =~ /div[$1]/div[($1+1)]/e;
}
else {
$arr[13] = div[2];
}
Now that the 14th element is changed, concatenate the array to get the new output string for the portion from the portion between div[10] and the 14th one:
{
local $" = '';
$newstring = "#arr";
}
splice($originalstring,$orig_index,$len,$newstring);
I think that will do.

Perl Array Values Access and Sum by each unique key

# my code as follows
use strict;
use FileHandle;
my #LISTS = ('incoming');
my $WORK ="c:\";
my $OUT ="c:\";
foreach my $list (#LISTS) {
my $INFILE = $WORK."test.dat";
my $OUTFILE = $OUT."TEST.dat";
while (<$input>) {
chomp;
my($f1,$f2,$f3,$f4,$f5,$f6,$f7) = split(/\|/);
push #sum, $f4,$f7;
}
}
while (#sum) {
my ($key,$value)= {shift#sum, shift#sum};
$hash{$key}=0;
$hash{$key} += $value;
}
while my $key (#sum) {
print $output2 sprintf("$key1\n");
# print $output2 sprintf("$key ===> $hash{$key}\n");
}
close($input);
close($output);
I am getting errors Unintialized error at addition (+) If I use 2nd print
I get HASH(0x19a69451) values if I use 1st Print.
I request you please correct me.
My output should be
unique Id ===> Total Revenue ($f4==>$f7)
This is wrong:
"c:\";
Perl reads that as a string starting with c:";\n.... Or in other words, it is a run away string. You need to write the last character as \\ to escape the \ and prevent it from escaping the subsequent " character
You probably want to use parens instead of braces:
my ($key, $value) = (shift #sum, shift #sum);
You would get that Unintialized error at addition (+) warning if the #sum array has an odd number of elements.
See also perltidy.
You should not enter the second while loop :
while my $key (#sum) {
because the previous one left the array #sum empty.
You could change to:
while (<$input>) {
chomp;
my #tmp = split(/\|/);
$hash{$tmp[3]} += $tmp[6];
}
print Dumper \%hash;