What is the proper way to search array using Smart Match? - arrays

I'm new to programming much less Perl; I'm having difficulty with searching an array I've made from an external text file. I'm looking for a simple way to check if the user entry is located in the array. I've used the Smart Match function before but never in an "if" statement and can't seem to get it to work. Am I implementing this function wrong, or is there an easier way to check if the user's string is in the array?
#!/usr/bin/perl
use 5.010;
#Inventory editing script - Jason Black
#-------------------------------------------------------------------------------
print "1. Add Items\n";
print "2. Search Items\n";
print "Please enter your choice: ";
chomp ($userChoice = <STDIN>); #Stores user input in $userChoice
if($userChoice == 1){
$message = "Please enter in format 'code|title|price|item-count'\n";
&ChoiceOne;
}
elsif($userChoice == 2){
$message = "Enter search terms\n";
&ChoiceTwo;
}
sub ChoiceOne{
print "$message\n";
chomp($userAddition = <STDIN>); #Stores input in $userAddition
$string1 = "$userAddition";
open (FILE, "FinalProjData.txt") or die ("File not found"); #"FILE" can be named anything
#array = <FILE>;
if ( /$string1/ ~~ #array){
print "This entry already exists. Would you like to replace? Y/N \n";
chomp($userDecision = <STDIN>); #Stores input in $userDecision
if ($userDecision eq "Y"){
$string1 =~ s/$userAddition/$userAddition/ig;
print "Item has been overwritten\n";}
elsif($userDecision eq "N"){
print FILE "$string1\n";
print "Entry has been added to end of file.\n";}
else{
print "Invalid Input";
exit;}
}
else {
print FILE "$string1\n";
print "Item has been added.\n";}
close(FILE);
exit;
}#end sub ChoiceOne
sub ChoiceTwo{
print "$message\n";
}

If you want to avoid using smartmatch alltogether:
if ( grep { /$string1/ } #array ) {
To actually match the $string1, however, it needs to be escaped, so that | doesn't mean or:
if ( grep { /\Q$string\E/ } #array ) {
or just a simple string compare:
if ( grep { $_ eq $string } #array ) {

Related

loop through elements of array to find character perl

I have a perl array where I only want to loop through elements 2-8.
The elements are only meant to contain numbers, so if any of those elements contain a letter, I want to set an error flag = 1, as well as some other variables as seen.
The reason I have 2 error flag variables is due to scope rules within the loop.
fields is an array, I created by splitting another irrelevant array by the " " key.
So, when I try to print error_line2, error_fname2 from outside the loop, I get this:
Use of uninitialized value $error_flag2 in numeric eq (==)
I don't know why, because I've initialized the value within the loop and created the variable outside the loop.
Not sure if I'm even looping to find characters correctly, so then it's not setting the error_flag2 = 1.
Example line:
bob hankerman 2039 3232 23 232 645 64x3 324
since element 7 has the letter 'x' , I want the flag to be set to 1.
#!/usr/bin/perl
use strict;
use warnings;
use Scalar::Util qw(looks_like_number);
my $players_file = $ARGV[0];
my #players_array;
open (my $file, "<", "$players_file")
or die "Failed to open file: $!\n";
while(<$file>) {
chomp;
push #players_array, $_;
}
close $file;
#print join "\n", #players_array;
my $num_of_players = #players_array;
my $error_flag;
my $error_line;
my $error_fname;
my $error_lname;
my $error_flag2=1;
my $error_line2;
my $error_fname2;
my $error_lname2;
my $i;
foreach my $player(#players_array){
my #fields = split " ", $player;
my $size2 = #fields;
for($i=2; $i<9; $i++){
print "$fields[$i] \n";
if (grep $_ =~ /^[a-zA-Z]+$/){
my $errorflag2 = 1;
$error_flag2 = $errorflag2;
my $errorline2 = $player +1;
$error_line2 = $errorline2;
my $errorfname2 = $fields[0];
$error_fname2 = $errorfname2;
}
}
if ($size2 == "9" ) {
my $firstname = $fields[0];
my $lastname = $fields[1];
my $batting_average = ($fields[4]+$fields[5]+$fields[6]+$fields[7]) / $fields[3];
my $slugging = ($fields[4]+($fields[5]*2)+($fields[6]*3)+($fields[7]*4)) / $fields[3];
my $on_base_percent = ($fields[4]+$fields[5]+$fields[6]+$fields[7] +$fields[8]) / $fields[2];
print "$firstname ";
print "$lastname ";
print "$batting_average ";
print "$slugging ";
print "$on_base_percent\n ";
}
else {
my $errorflag = 1;
$error_flag = $errorflag;
my $errorline = $player +1;
$error_line = $errorline;
my $errorfname = $fields[0];
$error_fname = $errorfname;
my $errorlname = $fields[1];
$error_lname = $errorlname;
}
}
if ($error_flag == "1"){
print "\n Line $error_line : ";
print "$error_fname, ";
print "$error_lname :";
print "Line contains not enough data.\n";
}
if ($error_flag2 == "1"){
print "\n Line $error_line2 : ";
print "$error_fname2, ";
print "Line contains bad data.\n";
}
OK, so the problem you've got here is that you're thinking of grep in Unix terms - a text based thing. It doesn't work like that in perl - it operates on a list.
Fortunately, this is pretty easy to handle in your case, because you can split your line into words.
Without your source data, this is hopefully a proof of concept:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
while ( <DATA> ) {
#split the current line on whitespace into an array.
#first two elements get assigned to firstname lastname, and then the rest
#goes into #values
my ( $firstname, $lastname, #values ) = split; #works on $_ implicitly.
#check every element in #values, and test the regex 'non-digit' against it.
my #errors = grep { /\D/ } #values;
#output any matches e.g. things that contained 'non-digits' anywhere.
print Dumper \#errors;
#an array in a scalar context evaluates as the number of elements.
#we need to use "scalar" here because print accepts list arguments.
print "There were ", scalar #errors, " errors\n";
}
__DATA__
bob hankerman 2039 3232 23 232 645 64x3 324
Or reducing down the logic:
#!/usr/bin/perl
use strict;
use warnings;
while ( <DATA> ) {
#note - we don't need to explicity specify 'scalar' here,
#because assigning it to a scalar does that automatically.
#(split) splits the current line, and [2..8] skips the first two.
my $count_of_errors = grep { /\D/ } (split)[2..8];
print $count_of_errors;
}
__DATA__
bob hankerman 2039 3232 23 232 645 64x3 324
First : You don't need to use "GREP", Simply you can match the string with "=~" in perl and you can print matched value with $&.
Second : You should use $_ if and only if there is not other variable used in the loop. There is already $i used in the loop, you can write the loop as :
for my $i (2..9) {
print "$i\n";
}
or
foreach(2..9) {
print "$_\n";
}

Updated file strings words together

My script works, except when I append the file: it bunches my words into one string like so:
Joe Smo45MaleSingle
Originally, in the text file, they are in a column. I want the appended file to stay in a column.
#!/usr/bin/perl
use strict;
use warnings;
use File::Slurp;
my #array1 = ("Full Name:", "Age:", "Gender:", "Marital Status:");
my #array2 = read_file("empdata.txt", chomp => 1);
print "$array1[0] $array2[0]\n";
print "$array1[1] $array2[1]\n";
print "$array1[2] $array2[2]\n";
print "$array1[3] $array2[3]\n";
print "Do you want to change the age? (y or n) :";
chomp(my $answer = <STDIN>);
if ($answer eq "y") {
print "What is the new age? :";
chomp(my $age = <STDIN>);
$array2[1] = $age;
write_file("empdata.txt",#array2);
print "Do you want to change marital status? (y or n) :";
chomp(my $answer = <STDIN>);
if ($answer eq "y") {
print "What is the new marital status? :";
chomp(my $status = <STDIN>);
$array2[3] = $status;
write_file("empdata.txt",#array2);
}
} else {
print "Do you want to change marital status? (y or n) :";
chomp(my $answer = <STDIN>);
if ($answer eq "y") {
print "What is the new marital status? :";
chomp(my $status = <STDIN>);
$array2[3] = $status;
write_file("empdata.txt",#array2);
}
}
close || die "could not close file";
I'm guessing, because you haven't shown us your input data. But I think you're saying that your input file looks like this:
Joe Smo
45
Male
Single
And, after running your program, you end up with this:
Joe Smo45MaleSingle
When you read the file, you use the chomp => 1 option to read_file(). That removes newlines from the end of the lines in the file. But write_file() doesn't have an unchomp option to replace the newlines.
The quickest, dirtiest, fix is to stop removing the newlines. You'll want to remove the chomp => 1 option from read_file() and also stop calling chomp() on the new $status values as you read them in. You can also remove the "\n" from the lines where you are printing the original records (as they will have a newline attached).
However, I'd caution you against using File::Slurp. It has some problems with UTF8 data that are likely to burn you in the future. I'd recommend Path::Tiny (which has slurp() and spew() methods) in its place.
But, really, it looks like you're writing a database. So why not use a database?

Perl: How to reference to an array?

I have a program here that will allow user to enter in words that will be used as filenames later on. The user's input is inserted into an array. I then create a simple menu asking what the user wants to do with the "filename": create, reame, copy, and delete. Using subroutines, I believe I've successfully executed the code. However, I am having trouble trying to reference the array element.
In short, how do I create, rename, copy, and delete the filenames that are saved in the array? The issues I cant resolve are asterisked
#!/usr/bin/perl
use strict;
use warnings;
use File::Copy qw/copy/;
my $new;
my $old;
my $file;
print "Enter number of lines\n";
chomp(my $n = <STDIN>);
my #lines;
print "Enter some words!\n";
for (1..$n) {
chomp(my $input = <STDIN>);
#will add the user input at the beginning of the array
push #lines, $input;
}
print "#lines\n";
#create a menu
my $in = '';
print "1. Create\n2. Rename\n3. Copy\n4. Delete\n5. Quit\n";
while ($in ne "quit")
{
print "\nEnter the your choice:\n";
chomp($in = <STDIN>);
print "For which file do you want to $in?\n";
print "#lines\n";
**#I need to reference the array here***********************
if ($in eq "create") {
***How to create the file that the user inputted?*****************
&createFile;
print "\nFile has been created\n";
}
elsif ($in eq "rename") {
#print "Enter the old name\n";
# chomp($old = <STDIN>);
#*********refer to array of FILES*********************
print "Enter the new name for this file!\n";
chomp($new = <STDIN>);
&renameFile;
print "File has successfully been renamed.";
}
elsif ($in eq "copy") {
#********file and array refering***************************
&copyFile;
print "Hopefully, the file has beed copied!";
}
elsif ($in eq "delete") {
#******refer to file from user*******************
&deleteFile;
print "Deleted";
}
}
#create subroutine
sub createFile {
open('>' .*****issue here too**) or die "\nCannot create";
}
sub renameFile {
rename ($old, $new);
}
sub copyFile {
copy $old, $new;
}
sub deleteFile {
#unlink *****************************
}
First line is $line[0], second is $line[1] and so on...
You can get user choice like this:
$line[$in]
About how to refer to perl data structures see here
$lines[0]; #First line
$lines[1]; #Second line
$array = \#lines; # Get reference to the list of lines
$array->[0]; #First line
$array->[1]; #Second line
to create file and put data into just use:
open $fh, '>filename';
print $fh #lines;
print $fh #$array; # ... or same for reference
Notice lack of coma after $fh

Building a Perl program to acess a set of data

I have to write a Perl program that is able to read the social security baby names for a specific year, and have a person be able to enter a name, and be told weather it is male or female, how many people were born with that name, and where it falls in rank.
So far I have been able to separate the data into two separate arrays based on gender, but have to no idea where to go from here.
#!/usr/local/bin/perl
use strict;
use warnings;
open (FILE, "ssbn1898.txt");
print <FILE>;
close (FILE);
my #M_array;
my #F_array;
open (my $input, "<", 'ssbn1898.txt');
while ( <$input> ) {
chomp;
my ( $name, $gender ) = split ( /,/ );
if ( $gender = "M" ) {
push ( #M_array, $name );
}
else {
push ( #F_array, $name );
}
}
close ( $input );
print 'M: ' . join("\t", #M_array) . "\n";
print 'F: ' . join("\t", #F_array) . "\n";
http://www.ssa.gov/cgi-bin/popularnames.cgi
This is the data I am working with.
So far you have 2 arrays for each gender. Now the pending tasks are
Input: Allow users to input a name
Output: Male or Female
Output: Number of people with that name
To take input from user you could do
my $userinput = <STDIN>;
Then you might need to chomp the $userinput and then check whether the $userinput is in array 1 or array 2. For that you will have to use grep or loops. Doing that you'd be able to find whether name belongs to array with Male names or Females.
if (grep /$userinput/, #male_names) {
print "found $userinput in male list\n";
}
Using loop you could do something like below to find total number of people with that name:
foreach (#male_names){
$counter++ if $userinput eq $_;
}
PS: grep returns list, so if you use a scalar you could find number of matches, so you don't have to go for loops.
#!/usr/bin/perl
use strict;
use warnings;
my #male_names = qw(Raj Rohan John Jim Tony Raj Rohan Jim Jim);
my #female_names = qw(Natasha Neha Neha Jasmine Rita Rosy);
my $matches;
my $userinput = <STDIN>;
chomp ($userinput);
if ( $matches = grep /$userinput/, #male_names ) {
print "found $userinput in male list, count is $matches \n";
}
elsif ( $matches = grep /$userinput/, #female_names ) {
print "found $userinput in female list, count is $matches \n";
}
else{
print "Did not find name";
}

Perl read a file and an array and find common words

This is kind of a small issue and I hope you are able to help me. My code is probably rubbish. For an example, I have a file in which the only statement is John is the uncle of Sam. My Perl script should copy the file contents into an array. User should be able to input different names and search if those names are mentioned in the file. There should be an array with relationships like "uncle aunt, mother, father etc" in the program.
#use warnings;
use Array::Utils qw(:all);
print "Please enter the name of the file\n";
my $c = <STDIN>;
open(NEW,$c) or die "The file cannot be opened";
#d = <NEW>;
print #d, "\n";
#g = qw(aunt uncle father);
chomp #d;
chomp #g;
my $e;
my $f;
print "Please enter the name of the first person\n";
my $a = <STDIN>;
print "Please enter the name of the second person\n";
my $b = <STDIN>;
my #isect = intersect(#g, #d);
print #isect;
foreach(#d)
{
if ($a == $_)
{
$e = $a;
}
else
{
print "The first person is not mentioned in the article";
exit();
}
if ($b == $_)
{
$f = $b;
}
else
{
print "The second person is not mentioned in the article";
exit();
}
}
print $e;
print $f;
close(NEW);
This is something that I have done so far, the intersection is not giving the word uncle which is the word common in both arrays. The program is taking any random name and printing them. It is not saying that the name doesn't exist in the file when I enter a different name other than John and Sam
There are several problems:
You do not chomp $c. The filename contains a newline at the end.
You use the 2-argument form of open, but do not test the second argument. This is a security problem: do you know what happens if the user input contains > or |?
You use == to compare strings. String equality is tested with eq, though, == tests numbers.
Moreover, you do not want to know whether "Sam" equals to "John is the uncle of Sam". You want to know whether it is a part of it. You might need to use index or regular expressions to find out.
Do not use $a as the name of a variable, it is special (see perlvar).
Do not try to compare strings with ==! Use eq (equals) instead. Also you didnt chomp your input $a$b`. I think this is what you're trying to do:
#!/usr/bin/perl
use strict;
use warnings;
print "Please enter the name of the file\n";
my $c = <STDIN>;
open(NEW,$c) or die "The file cannot be opened";
my #d = <NEW>;
chomp #d;
my $e;
my $f;
print "Please enter the name of the first person\n";
my $aa = <STDIN>;
print "Please enter the name of the second person\n";
my $bb = <STDIN>;
chomp $aa;
chomp $bb;
my $pattern_a = quotemeta $aa;
my $pattern_b = quotemeta $bb;
foreach (#d){
if ($_ =~ /$pattern_a/){
$e = $aa;
}
elsif ($_ =~ /$pattern_b/){
$f = $bb;
}
}
close(NEW);
unless ($e){
print "First person not mentionend\n";
}
unless ($f){
print "Second person not mentioned\n";
}

Resources