How to create Hash with single key and multiple values? - perl

I have the database which I am accessing, my goal is to store every row in hash where id would be key and other things present in the row i.e. other columns as values.
Actually I am parsing the XLSX sheet and storing its values, after that have to apply logic to fetch database values and later on compare values of XLSX sheet and DB, if they are equal then test is pass else fail. I almost spent 3 days but not getting any results.
Output
#carrierValuesAll[0] = 1 1 https://au-sbc.trustidinc.com/tid sbcLabStub sbcLab SKY 0 2019-11-07 20:10:43 2021-07-02 04:39:43 TrustID Lab Oracle Y Y Y ivr.localdomain Y trustid
#carrierValuesAll1 = 2 1 https://au-sbc.trustidinc.com/tid sbcLab sbcLab SKY 2 2019-11-07 20:10:43 2020-12-14 06:24:17 TrustID Lab Oracle Y Y Y ivr.localdomain Y admin
Desired Output
Hash --> Key = 1Values = 1,https://au-sbc.trustidinc.com/tid,sbcLabStub,sbcLab,SKY,0,2019-11-07,20:10:43,2021-07-02,04:39:43, TrustID, Lab Oracle, Y, Y, Y, ivr.localdomain, Y, trustidSimilarly for other iterations or id's in loop where id 2 will be key 2 and its values are key 2 values.Table looks likeThe code I am using for this is below:
#!/usr/bin/perl
use DBI;
use strict;
use warnings;
use Switch;
my #tstInfo;
my %DbHash;
my #carrierValuesAll=();
my #carrierValuesAllGet="";
if($dbType eq "mysql"){
#$database = "trustid";
$dsn = "DBI:$dbType:database=$database;host=$host:port=3306,DBSOCK = '/var/run/mysqld/mysqld.sock'";
$userid = "XXXX";
$password = 'XXXX';
}else{
$dbType="postgres";
$userid = "postgres";
$password = "postgres";
$dsn = "DBI:$dbType:database=$database;host=$host:port=5432";
}
my $dbcon = DBI->connect($dsn, $userid, $password) or die "Can't connect to database: $DBI::errstr\n";
print "connected to the database\n";
my $notables = $dbcon->tables();
print "No of tables : $notables"."\n" ;
my #tables = $dbcon->tables(undef, undef, undef, 'TABLE');
my $len = #tables;
for (my $i = 0; $i < $len; $i = $i + 1)
{
print ("\#tables[$i] = $tables[$i]\n");
print("\n");
}
my $sth = $dbcon->prepare('select * from active_table_set');
$sth->execute();
while (my #row = $sth->fetchrow_array()) {
$database=join(',', #row);
print($database."\n");
if($dbType eq "mysql"){
#$database = "trustid";
$dsn = "DBI:$dbType:database=$database;host=$host:port=3306,DBSOCK = '/var/run/mysqld/mysqld.sock'";
$userid = "XXXX";
$password = 'XXXX';
}else{
$dbType="postgres";
$userid = "postgres";
$password = "postgres";
$dsn = "DBI:$dbType:database=$database;host=$host:port=5432";
}
$dbcon = DBI->connect($dsn, $userid, $password) or die "Can't connect to database: $DBI::errstr\n";
print "connected to the database\n";
#tables = $dbcon->tables(undef, undef, undef, 'TABLE');
$len = #tables;
for (my $i = 0; $i < $len; $i = $i + 1)
{
print ("\#tables[$i] = $tables[$i]\n");
print("\n");
if($tables[$i] eq '`trustid_b`.`carriers`'){
$sth = $dbcon->prepare('select * from carriers');
$sth->execute();
while (my #row = $sth->fetchrow_array()) {
push(#carrierValuesAll, [ #row ]);
}
}
}
}
$len = #carrierValuesAll;
for (my $i = 0; $i < $len; $i = $i + 1) {
print ("\#carrierValuesAll[$i] = #{$carrierValuesAll[$i]}\n");
print("\n");
push(#tstInfo,\#carrierValuesAll[$i]);
}
for(my $i = 0; $i <= $#tstInfo; $i++){
# push the test data on the array
$DbHash{$i+1} = $tstInfo[$i];
}
print(%DbHash).("\n");
Can I create the Hash Here
while (my #row = $sth->fetchrow_array()) {
push(#carrierValuesAll, [ #row ]);
}
Yes, Then How?
What I was doingStoring each row in scalar which was giving me output as below: Where, I was trying to push my each row in #carrierValuesAll and then pushing #carrierValuesAll in #tstInfo and trying to populate hash after that. I don't know how to access this, Whether this is correct or not. Please help me.

I have the database which I am accessing, my goal is to store every row in hash where id would be key and other things present in the row i.e. other columns as values
That's probably not what you want. What you actually want is a two-level hash, where the first level has the id as a key and a hash reference as the value. The second level hash would have column names as the keys mapping to the associated values.
One easy way to do that is to use fetchrow_hashref() instead of fetchrow_array().
my %carriers;
while (my $row = $sth->fetchrow_hashref) {
$carriers{$row->{id}} = $row;
}

Related

How to handle a Perl array the correct way?

I'm quite new in writing Perl code, so I'm hoping for some help here.
I'm querying a database, which works as long as I'm just selecting one row and write the queried variables into some variables in another database.
As soon as I want to create an array and do with multiple variables, what I am able to do with a single one, my code doesn't work.
Do you spot the mistake?
use strict;
use warnings;
use DBI;
my $host = "host";
my $database = "DBI:mysql:database";
my $tablename = "table";
my $user = "user";
my $pw = "password";
my $sql = "SELECT COUNT(`attribute`) FROM `table` WHERE `attribute` =".GETVALUE("attribute");
my $connect = DBI->connect($database, $user, $pw) or die "Couldn't connect to database: " . DBI->errstr;
my $sth = $connect->prepare($sql);
$sth->execute();
my $row;
my $sql2 = "SELECT `attribute2`,`name`, `adress` FROM `table` WHERE `attribute` =".GETVALUE("attribute");
my $sth2 = $connect->prepare($sql2);
$sth2->execute();
my(#attribute2, #name, #adress);
while (($row) = $sth->fetchrow()){
for (my $i = 0; $i < $row; $i++)
{
SETVALUE("Variable".$i , my $name->[$i].", ". my $adress->[$i]);
}
SETVALUE("CountAttribute", $row);
}
`
I already tried to exchange "fetchrow" with "fetchrow_array" and "fetchrow_arrayref", but this didnt work either.
Accepting the comment that fetchrow() is an undocumented alias for fetchrow_array():
The fetchrow_array() method returns the contents of the row, not an index. Assuming what you want to do is call SETVALUE() with each name and address, and then one last time with the number of rows fetched, your while() loop should be something along the lines of:
my $i = 0;
while ( my ( $attr2, $name, $addr ) = $sth->fetchrow_array() ) {
SETVALUE( "Value$i", "$name, $addr" );
$i++;
}
SETVALUE( "CountAttribute", $i );

Combine an Array object with scalar value within a scalar? Perl

I'm trying to combine an item from an Array list with a scalar $range. Here's how I'm trying to do it.
my $rowinc = 2;
my $colarray = #collet[0];
my $range = $colarray $rowinc;
chomp $range;
$sheet->Range($range)->{Value} = $ir;
shift #collet;
$sheet->Range($range)->{Value} = $sn;
shift #collet;
$sheet->Range($range)->{Value} = (join(", ", #parts));
shift #collet;
$sheet->Range($range)->{Value} = $ref;
$rowinc++;
unshift #collet, 'C';
unshift #collet, 'B';
unshift #collet, 'A';
I've tried multiple ways of doing this and to no avail. Here's the error I receive while running this particular snippet.
Scalar found where operator expected at gen1.pl line 87, near "$colarray $rowinc
"
(Missing operator before $rowinc?)
syntax error at gen1.pl line 87, near "$colarray $rowinc"
Execution of gen1.pl aborted due to compilation errors.
Press any key to continue . . .
I'm assuming that the array can't be used in that manner to denote the value for $range. The problem I'm running into is that I am using Win32::OLE to manage my excel spreadsheets because it gives me the ability to open an already existing spreadsheet. But the drawback is I cannot enter my cell ranges as integers ($row,$col) I've tried this just incrementing $row and $col I want to be able to manage this effectively instead of using a bunch of if else and what not.
What I've tried to do is my #collet = ('A', 'B', 'C', 'D');
tells me which column to print in if I start at 0 it should start printing in A col. which is good and then every time it prints in a column it shifts right so now #collet[0] should be 'B'. I know this isn't the best method but I've changed my original method to this in hopes to solve the issue. any help would be awesome!
Here's my full script for some context.
#!C:\Perl\bin
#manage IR/SRN/Parts Used
#Written for Zebra
#Author Dalton Brady
#Location Bentonville AR
use strict;
use warnings;
use POSIX qw(strftime);
use Win32::OLE;
use Win32::OLE qw( in with);
use Win32::OLE::Const 'Microsoft Excel';
use Win32::OLE::Variant;
$Win32::OLE::Warn = 3; #Die on errors
#different types of units worked on, trying to name the worksheet
#after one depending on user input have yet to add that func.
my #uut = ('VC5090', 'MK2046', 'MK2250', 'MK4900', '#pos');
my $ref = strftime '%Y-%m-%d', localtime(); #create the datestamp
my $i = 0; #increment counter for why loop
my $n = 0; #increment for do until loop
my $xfile = 'X:\dataTest\data.xls'; #location of excel file
my $book; #place for the workbook to exist
my $sheet; #worksheet exists here
my $ex; #a place to hold the excel application
my $row = 2; #track row in which data will
#be placed with the do until loop
my $col = 1;
my #parts ; # store the different parts as a list within
# an area (to be written to the spreadsheet)
my #talk = ( 'IR:', 'SN:',
'#of Parts Used: ', 'PN:',
'Units Completed: ');
my #collet = "A" .. "Z"
# start an instance of excel
$ex = Win32::OLE->new('Excel.Application');
$ex->{DisplayAlerts} = 0; #turn off alerts/textboxes/saveboxes
#check to see if excel file exists if yet open it, if not create it
if (-e $xfile) {
$book = $ex->Workbooks->Open($xfile);
$sheet = $book->Worksheets("Test");
$sheet->Activate();
}
else {
$book = $ex->Workbooks->Add()
; #create new workbook to be used because we couldn't find one
#########SETUP YOUR EXCEL FILE#############
$sheet = $book->Worksheets("Sheet1");
$sheet->Activate();
$sheet->{Name} = "Test";
$sheet->Cells("a1")->{HorizontalAlignment} = x1HAlignCenter;
$sheet->Cells("b1")->{HorizontalAlignment} = x1HAlignCenter;
$sheet->Cells("c1")->{HorizontalAlignment} = x1HAlignCenter;
$sheet->Columns("a")->{ColumnWidth} = 20;
$sheet->Columns("b")->{ColumnWidth} = 20;
$sheet->Columns("c")->{ColumnWidth} = 30;
$sheet->Range("a1")->{Value} = "IR Number";
$sheet->Range("b1")->{Value} = "Serial Number";
$sheet->Range("c1")->{Value} = "Parts Used";
$sheet->Range("d1")->{Value} = "Date";
$book->SaveAs($xfile); #Save the file we just created
}
# ask for how many units user will be
# scanning or has completed to be scanned
print $talk[4] ;
#unit count tracker, determines how many times the do while loop runs
my $uc = <>;
do {
print $talk[0]; #ask for the IR number
my $ir = <>;
chomp $ir;
print $talk[1]; #ask for uut Serial Number
my $sn = <>;
chomp $sn;
print $talk[2];
# ask for the number of parts used, to regulate
# the parts list storage into the #parts array
my $pu = <>;
while ($i < $pu) {
print $talk[3];
my $scan = <>;
chomp $scan;
push #parts, $scan;
$i++;
}
my $rowinc = 2;
my $colarray = #collet[0];
my $range = $colarray $rowinc;
chomp $range;
$sheet->Range($range)->{Value} = $ir;
shift #collet;
$sheet->Range($range)->{Value} = $sn;
shift #collet;
$sheet->Range($range)->{Value} = (join(", ", #parts));
shift #collet;
$sheet->Range($range)->{Value} = $ref;
$rowinc++;
unshift #collet, 'C';
unshift #collet, 'B';
unshift #collet, 'A';
} until ($n == $uc);
# save and exit
$book->SaveAs($xfile);
$book = $ex->WorkBooks->Close();
undef $book;
undef $ex;

How I can take the coordinates of a block of numbers?

I have a problem tha bothers me a lot...
I have a file with two columns (thanks to your help in a previous question) like:
14430001 0.040
14430002 0.000
14430003 0.990
14430004 1.000
14430005 0.050
14430006 0.490
....................
the first column is coordinates the second probabilities.
I am trying to find the blocks with probability >=0.990 and to be more than 100 in size.
As output I want to be like this:
14430001 14430250
14431100 14431328
18750003 18750345
.......................
where the first column has the coordinate of the start of each block and the second the end of it.
I wrote this script:
use strict;
#use warnings;
use POSIX;
my $scores_file = $ARGV[0];
#finds the highly conserved subsequences
open my $scores_info, $scores_file or die "Could not open $scores_file: $!";
#open(my $fh, '>', $coords_file) or die;
my $count = 0;
my $cons = "";
my $newcons = "";
while( my $sline = <$scores_info>) {
my #data = split('\t', $sline);
my $coord = $data[0];
my $prob = $data[1];
if ($data[1] >= 0.990) {
#$cons = "$cons + '\n' + $sline + '\n'";
$cons = join("\n", $cons, $sline);
# print $cons;
$count++;
if($count >= 100) {
$newcons = join("\n", $newcons, $cons);
my #array = split /'\n'/, $newcons;
print #array;
}
}
else {
$cons = "";
$count = 0;
}
}
It gives me the lines with probability >=0.990 (the first if works) but the coordinates are wrong. When Im trying to print it in a file it stacks, so I have only one sample to check it.
Im terrible sorry if my explanations aren't helpful, but I am new in programming.
Please, I need your help...
Thank you very much in advance!!!
You seem to be using too much variables. Also, after splitting the array and assigning its parts to variables, use the new variables rather than the original array.
sub output {
my ($from, $to) = #_;
print "$from\t$to\n";
}
my $threshold = 0.980; # Or is it 0.990?
my $count = 0;
my ($start, $last);
while (my $sline = <$scores_info>) {
my ($coord, $prob) = split /\t/, $sline;
if ($prob >= $threshold) {
$count++;
defined $start or $start = $coord;
$last = $coord;
} else {
output($start, $last) if $count > 100;
undef $start;
$count = 0;
}
}
output($start, $last) if $count > 100;
(untested)

exists statement not working perl

This is a homework assignment. I am not looking for the "code to make it work" more looking for a point in the right direction on where my logic is wrong.
use strict;
use warnings;
#rot13 sub for passwords
sub rot13{
my $result;
chomp(my $input = <STDIN>);``
# all has to be lower case
my $lower = lc $input;
my $leng = length $lower;
for(my $i = 0; $i < $leng; $i++){
my $temp = substr ($lower,$i,1);
my $con = ord $temp;
if($con >= '55'){
if($con >= '110'){
$con -= 13;
}
else{
$con += 13;
}
}
$result = $result . chr $con;
}
return $result;
};
#opening a file specified by the user for input and reading it
#into an array then closing file.
open FILE, $ARGV[0] or die "cannot open input.txt";
my #input = <FILE>;
close FILE;
my (#username,#password,#name,#uid,#shell,#ssn,#dir,#group,#gid);
my $ui = 100;
foreach(#input){
my ($nam, $ss, $gro) = split ('/', $_);
chomp ($gro);
$nam= lc $nam;
I created a hash so I can use the exists function then using the function and if it does exist go to the next round of the loop. I feel like I am missing something with this.
my %nacheck;
if( exists ($nacheck { '$nam' } )){
next;
}
$nacheck{ "$nam" } = 1;
while (my ($key, $value) = each %nacheck){
print "$key => $value\n";
}
All this works for now but any tips on how to do it better would be appreaciated
my($unf, $unm, $unl) = split (/ /, $nam);
$unf = (substr $unf,0,1);
$unm = (substr $unm,0,1);
$unl = (substr $unl,0,1);
my $un = $unf . $unm . $unl;
if(($gro) eq "faculty"){
push #username, $un;
push #gid, "1010";
push #dir, "/home/faculty/$un";
push #shell, "/bin/tcsh";
}
else{
my $lssn = substr ($ss,7,4);
push #username, $un . $lssn;
push #gid, "505";
push #dir, "/home/student/$un";
push #shell, "/bin/bash";
}
#pushing results onto global arrays to print out later
push #ssn, $ss;
my $pass = rot13;
push #password, $pass;
push #name, $nam;
push #uid, $ui += 1;
}
#printing results
for(my $i = 0; $i < #username; $i++){
print
"$username[$i]:$password[$i]:$uid[$i]:$gid[$i]:$name[$i]:$dir[$i]:$shell[$i]\n";
}
The value of the expression '$nam' is those four characters themselves. The value of the expression "$nam" is whatever the value of the variable $nam is, expressed as a string.
Double quotes allow string interpolation. Single quotes do not; you get exactly what you type.
As you've written it:
my %nacheck;
if( exists ($nacheck { '$nam' } )){
next;
}
$nacheck{ "$nam" } = 1;
the %nacheck is newly created and must be empty. Therefore the exists test fails.
Or have you just shown the definition adjacent to the test for the purpose of the example?
If so, can you show us what your code actually looks like?
Edit: Also, as Charles Engelke noted, you've used single-quotes around a variable '$nam' which is wrong.

making cells blank in and excel sheet using perl

I have an excel sheet to which I need to empty some cells
So far this is what it looks like:
I open the sheet, and check for cells not empty in column M.
I add those cells to my array mistake
and then I would like to make black all those cells and save the file (this step not working), as that file needs to be the input to anotherprogram/
thanks!
$infile = $ARGV[0];
$columns = ReadData($infile) or die "cannot open excel table\n\n";
print "xls sheet contains $columns->[1]{maxrow} rows\n";
my $xlsstartrow;
if ( getExcel( A . 1 ) ne "text" ) {
$xlsstartrow = 2;
}
else
{
$xlsstartrow = 4;
}
check_templates();
print "done";
sub check_templates {
for ( $row = $xlsstartrow ; $row < ( $columns->[1]{maxrow} + 1 ) ; $row++ ) {
if (getExcel(M . $row) ne "" ){
$cell = "M" . $row ;
push(#mistakes,$cell);
}
}
rewritesheet(#mistakes);
}
sub rewritesheet {
my $FileName = $infile;
my $parser = Spreadsheet::ParseExcel::SaveParser->new();
my $template = $parser->Parse($FileName);
my $worksheet = $template->worksheet(0);
my $row = 0;
my $col = 0;
# Get the format from the cell
my $format = $template->{Worksheet}[$sheet]
->{Cells}[$row][$col]
->{FormatNo};
foreach (#mistakes){
$worksheet->AddCell( $_, "" );
}
$template->SaveAs($infile2);`
Empty column values in an Excel sheet and save the result?
If the whole purpose of your program is to delete all column M values from a .xls file, then the following program (adopted from your program) will do exactly that:
use strict;
use warnings;
use Spreadsheet::ParseExcel;
use Spreadsheet::ParseExcel::SaveParser;
my $infile = $ARGV[0];
(my $infile2 = $infile) =~ s/(\.xls)$/_2$1/;
my $parser = Spreadsheet::ParseExcel::SaveParser->new();
my $workbook = $parser->Parse($infile);
my $sheet = $workbook->worksheet(0);
print "xls sheet contains rows \[0 .. $sheet->{MaxRow}\]\n";
my $startrow = $sheet->get_cell(0, 0) eq 'text' ? 4-1 : 2-1;
my $col_M = ord('M') - ord('A');
for my $row ($startrow .. $sheet->{MaxRow}) {
my $c = $sheet->get_cell($row, $col_M);
if(defined $c && length($c->value) > 0) { # why check?
$sheet->AddCell($row, $col_M, undef) # delete value
}
}
$workbook->SaveAs($infile2);
print "done";
But, if you really want to clear out column M only, why would you test for values? You could just overwrite them without test. Maybe thats not all your program is required to perform? I don't know.
Regards
rbo