Redacting one array with modified instances of another - arrays

Hello I have a problem that I am trying to solve.
Define a method called clean_slate that will take a String representing a record and another String representing a name as arguments. The method returns a string representing the original record, but with every instance of the name replaced with twice as many X's as the name contains characters. If a full name is passed, it will also replace instances where only the first or last name is used with as many X's as the full name contains characters. Your method should not be case sensitive. Assume that the inputs will always be a String. Return the original record if the name isn't found.
So far I have:
def clean_slate(record, name)
name = name.downcase
split_name = name.downcase.split(" ") #array
change_name = split_name.map do |letter|
letter = "X" * 2 * name.length
end
record_words = record.downcase.split(" ") #array
new_record = record_words.map do |word|
if word.include?(split_name[0] || split_name[1])
word = change_name
else
word
end
end
new_record.join(" ")
end
clean_slate("Megan Fox is a talented actress. megan knows sulfoxide is a chemical compound containing a sulfinyl (SO) functional group attached to two carbon atoms. It is a polar functional group. Ms. fox has many talents.", "Megan Fox")
I am returning the record but I am not getting the last name in the record redacted. Does anyone have any idea how I can fix that?
"XXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXX fox is a talented actress. XXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXX knows sulfoxide is a chemical compound containing a sulfinyl (so) functional group attached to two carbon atoms. it is a polar functional group. ms. fox has many talents."

Within your record.split.map you need to check if the record.downcase being passed include the first or last value for split, but at the same time to check if the word in the block is equal to any of those two, so it would be something like:
new_record = record.split.map do |word|
if record.downcase.include?(split_name[0] || split_name[1]) &&
word.downcase == split_name[0] || word.downcase == split_name[1]
...
Also if you use map over split_name to assign the amount of X, it'll return an array, and when you do word = change_name, it takes the value inside an array, as ['XXXXX'] (e.g), so you need to access the only one value inside:
word = change_name.first
As it's not needed to initialize a variable not being used inside the block, then just change_name.first.
This way, this works:
def clean_slate(record, name)
name = name.downcase
split_name = name.downcase.split(" ")
change_name = split_name.map do |letter|
letter = 'X' * 2 * name.length
end
new_record = record.split.map do |word|
if record.downcase.include?(split_name[0] || split_name[1]) &&
word.downcase == split_name[0] || word.downcase == split_name[1]
change_name.first
else
word
end
end
new_record.join(" ")
end
But it has some things you can improve:
split(" ") can be shortened as split
letter = 'X' * 2 * name.length can be 'XX' * name.length and
change_name = 'XX' * name.length no need to use map, so
change_name.first changes to change_name
join(" ") prefer single quotes instead double ones
You need a way to simplify the if statement inside the record.map and still you don't know if the name will be only two words to use [0] and [-1].
You could take a look to String#gsub.
def clean_slate(record, name)
record.gsub(/\b#{name.split.join('|')}\b/i, 'XX' * name.size)
end

Here's a cleaner way to implement this. Just compare each word in record to name and substitute the Xs.
def clean_slate record, name
names = name.downcase.split
new_record = []
record.split.each do |word|
new_record << word
if names.include?(word.downcase)
new_record[-1] = "X" * 2 * name.length
end
end
new_record.join(' ')
end
Result:
XXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXX is a talented actress. XXXXXXXXXXXXXXXXXX knows sulfoxide is a chemical compound containing a sulfinyl (SO) functional group attached to two carbon atoms. It is a polar functional group. Ms. XXXXXXXXXXXXXXXXXX has many talents.

If I understand your task right, I think you can do solution similar to it:
def clean_slate(record, name)
name = name.split.map(&:downcase)
record = record.split
record.map do |word|
if name.include? word.downcase
word = 'X' * 2 * name.join.length
else
word
end
end.join(' ')
end
#> clean_slate("Megan Fox is a talented actress. megan knows sulfoxide is a chemical compound containing a sulfinyl (SO) functional group attached to two carbon atoms. It is a polar functional group. Ms. fox has many talents.", "Megan Fox")
#=> "XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX is a talented actress. XXXXXXXXXXXXXXXX knows sulfoxide is a chemical compound containing a sulfinyl (SO) functional group attached to two carbon atoms. It is a polar functional group. Ms. XXXXXXXXXXXXXXXX has many talents."

Related

How can I loop through a concatenation

I'm trying to split a word with a '.' after every letter which I was successful in doing, however, my next step is to split the current splitted words again but I dont want to repeat variations.
my expected output is this:
input word: amaxa
first loop will give - a.maxa, am.axa, ama.xa, amax.a
then the next split should give - a.m.axa, a.ma.xa,a.max.a,
Essentially I wanted different variations of the word with '.' being added when a full loop had been exhausted however, my main issue was I had '.'s appearing next to each other and I tried to use a continue statement but it didn't work. Below is my source code
print("enter email without #gmail.com")
word = input("word: ")
lenofword = len(word) - 1
for i in range(0,lenofword):
sliceword = word[:1+i] + "." + word[1+i:]
lis.append(sliceword)
print(sliceword)
for j in range(0,lenofword):
slices = sliceword[:1+j] + "." + sliceword[j+1:]
if slices[i:] == slices[:]:
continue
print(slices)
ouput given:
enter email without #gmail.com
word: amax
a.max
am.ax
a.m.ax
am..ax
am..ax
ama.x
a.ma.x
am.a.x
ama..x
basically i want to get rid of the 'am..ax' and 'ama..x'
It is easier to choose the two locations where the dots should be placed (may be the same location for the single dot case). For example:
for i in range(1, lenofword + 1):
for j in range(i, lenofword + 1):
sliced = ".".join(filter(None, [word[:i], word[i:j], word[j:]]))
print(sliced)
This will print the following for the input word "amax":
a.max
a.m.ax
a.ma.x
am.ax
am.a.x
ama.x

Ruby Array Elements

I am trying to create password Generate in ruby. At the moment all is working just got stuck at the final piece of generating the password.
I asked user if he/she would like the password to include numbers, lowercase or uppercase.
If YES, user will enter 1 and 0 for NO.
I used the code below to generate password if everything is 1. Meaning user want to include numbers, lowercase and uppercase.
if numbers == 1 && lowercase == 1 && uppercase == 1
passGen = [(0..9).to_a + ('A'..'Z').to_a + ('a'..'z').to_a].flatten.sample(10)
end
p passGen
This works 90% of the time. 10% of the time the generated password will not include say any numbers. But everything else present. I am not sure if this is because of the size or length of Array from which the password is sampled.
Anyway lets go to the main problem below
Here is the problem, I am struggling to write the code to generate password if one or more of input is 0. That's if user don't want to include numbers. Or no numbers and uppercase etc . As I can't predict what user may want or not want. I need help on this please.
Thank you.
You will need to make your input array more dynamic:
passGen = []
passGen += (0..9).to_a if numbers == 1
passGen += ('A'..'Z').to_a if uppercase == 1
passGen += ('a'..'z').to_a if lowercase == 1
passGen.sample(10).join
Now, to tackle your other issue with missing characters - this is caused as you are simply taking 10 random characters from an array. So it can just take, for example, all digits.
To tackle this you need to get one character from each generator first and then generate the remaining characters randomly and shuffle the result:
def generators(numbers:, lowercase:, uppercase:)
[
(0..9 if numbers),
('A'..'Z' if uppercase),
('a'..'z' if lowercase)
].compact.map(&:to_a)
end
def generate_password(generators:, length:, min_per_generator: 1)
chars = generators.flat_map {|g| Array.new(min_per_generator) { g.sample }}
chars += Array.new(length - chars.length) { generators.sample.sample }
chars.shuffle.join
end
gens = generators(numbers: numbers == 1, uppercase == 1, lowercase: lowercase == 1)
Array.new(10) { generate_password(generators: gens, length: 10) }
The code doesn't know it needs to include a digit/letter from every group. The sample takes random signs and since you a basically sampling 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz there is a possibility that all the signs will not be digits.
The easiest way to fix it is to check if a sign from every group is in the "password" and then replace a random sign with a sign from group that is not present.
If I were to program this I would do it like that
def random_from_range(range)
range.to_a.sample.to_s
end
def passGen(numbers, lowercase, uppercase)
result = ''
possibleSigns = []
if numbers == 1
range = (0..9)
result += random_from_range(range)
possibleSigns += range.to_a
end
if lowercase == 1
range = ('A'..'Z')
result += random_from_range(range)
possibleSigns += range.to_a
end
if uppercase == 1
range = ('a'..'z')
result += random_from_range(range)
possibleSigns += range.to_a
end
desired_lenth = 10
while result.length < desired_lenth
result += possibleSigns.sample.to_s
end
result
end
puts passGen(1,1,1)
By saying (0..9).to_a + ('A'..'Z').to_a + ('a'..'z').to_a, you're creating an Array of 10 + 26 + 26 = 62 elements, and then you pick only 10 elements out of it.
At your place I'd wrap password generation around an until block:
def generate_password_with_digits_and_caps
[(0..9).to_a + ('A'..'Z').to_a + ('a'..'z').to_a].flatten.sample(10).join
end
passGen = ''
until passGen.match(/[A-Z]/) && passGen.match(/[a-z]/) && passGen.match(/\d/)
passGen = generate_password_with_digits_and_caps
end
This could also work (closer to your snipppet):
if numbers == 1 && lowercase == 1 && uppercase == 1
passGen = ''
until passGen.match(/[A-Z]/) && passGen.match(/[a-z]/) && passGen.match(/\d/)
passGen = [(0..9).to_a + ('A'..'Z').to_a + ('a'..'z').to_a].flatten.sample(10).join
end
end
Start with something simple and stupid:
passGen = (('0'..'9').to_a.sample(1)+ ('A'..'Z').to_a.sample(1)+('a'..'z').to_a.sample(8).shuffle).join
Technically speaking, this already fulfills your requirement. From the viewpoint of aesthetics and security, the disadvantage here is that the number of upper case characters is always 8. A more elegant solution would be to find three non-zero integers which add up to 10, and can be used as the arguments for the sample call. Also, if no numbers are requested, you simply pass 0 as argument to sample.
Since this exceeds the scope of your question, and I don't even know whether you want to go so far, I don't elaborate on this here further.

How to loop assigning characters in a string to variable?

I need to take a string and assign each character to a new string variable for a Text To Speech engine to read out each character separately, mainly to control the speed at which it's read out by adding pauses in between each character.
The string contains a number which can vary in length from 6 digits to 16 digits, and I've put the below code together for 6 digits but would like something neater to handle any different character count.
I've done a fair bit of research but can't seem to find a solution, plus I'm new to Groovy / programming.
OrigNum= "12 34 56"
Num = OrigNum.replace(' ','')
sNum = Num.split("(?!^)")
sDigit1 = sNum[0]
sDigit2 = sNum[1]
sDigit3 = sNum[2]
sDigit4 = sNum[3]
sDigit5 = sNum[4]
sDigit6 = sNum[5]
Edit: The reason for needing a new variable for each character is the app that I'm using doesn't let the TTS engine run any code. I have to specifically declare a variable beforehand for it to be read out
Sample TTS input: "The number is [var:sDigit1] [pause] [var:sDigit2] [pause]..."
I've tried using [var:sNum[0]] [var:sNum[1]] to read from the map instead but it is not recognised.
Read this about dynamically creating variable names.
You could use a map in your stuation, which is cleaner and more groovy:
Map digits = [:]
OrigNum.replaceAll("\\s","").eachWithIndex { digit, index ->
digits[index] = digit
}
println digits[0] //first element == 1
println digits[-1] //last element == 6
println digits.size() // 6
Not 100% sure what you need, but to convert your input String to output you could use:
String origNum = "12 34 56"
String out = 'The number is ' + origNum.replaceAll( /\s/, '' ).collect{ "[var:$it]" }.join( ' [pause] ' )
gives:
The number is [var:1] [pause] [var:2] [pause] [var:3] [pause] [var:4] [pause] [var:5] [pause] [var:6]

In Python3 display true if search term is in CSV table

I want a user to be able to search for a specific term and if it is in the database a message will be given saying that it is in the database.
This is the code that I have made so far and it runs without errors, but when I enter a search term it always says "We are sorry, but that does not seem to be in our system" and it always says this 3 times. I am using Pycharm as my IDE, but I don't think that this should make a difference.
I am semi new to Python so please be patient with me if I have missed something simple : )
import csv
uIn = input("Please enter a search term: ")
with open('database.csv', 'r') as uFile:
fileReader = csv.reader(uFile, delimiter=',')
for row in fileReader:
if uIn == row[1]: #True
print (uIn + "is in file")
else: #False
print("We are sorry, but that does not seem to be in our system")
----------------------------------------------------------Edit----------------------------------------------------------------
Thank you, #Rahul Bhatnagar, for your answer; it showed me that I needed to switch == with in.
This does answer part of the question, and I have figured out some of the other part, the else statement prints for every row that does not have the uIn inside of it. So say the uIn was on the third row the script would print the #False statement twice and the #True statement once. This is alrightish, but if the uIn is on say the 50th row or the 100th row then there would be 49 or 99 #false statements printed.
I would now say that about 50% of my problem is fixed, the part that is not fixed, is to only print 1 #true or 1 #false statement if it is in the file.
import csv
def search(uIn):
found = false;
with open('database.csv', 'r') as uFile:
fileReader = csv.reader(uFile, delimiter=',')
for row in fileReader:
if uIn in row: #True
found = true;
if(found):
print ("Found the string in file!")
else:
print("We are sorry, but that does not seem to be in our system")
uIn = raw_input("ST: ")
search(uIn)
Where my database.csv file is :
search,terms,go,into,this,file,in,this,format
You're trying to find uIn at position 1 of row in all cases, which is not going to be the situation.

Creating functions (Liberty Basic)

EDIT:
Ok I got how arrays work and that part seems to work. Im still having a problem with the functions. I do not understand how functions work correctly. I need to put something in the ( ) obviously but I dont understand what. You can see I have
search=findmonth()
Im not sure if that is even a good code to use I then have it where it calls up the function but of course because I dont know how to do functions it doesnt work. If someone can explain how functions work that would be great. All the information I have makes it really confusing and doesnt show real examples that explain anything.
This is the assingment, put it in code just so its smaller
Create two arrays
1. Monthname$(12) This array contains month names such as “January”, “February”, etc
2. MilesDriven(12) ‘ This array contains the miles driven for the month.
Notice that the MonthNames$(12) array is a string array while MilesDriven(12) is numeric array.
• Write a program to display the menu with the following options and ask for the user input.
Type P to populate miles and month name.
Type S to search for Month.
Type M to search for Month name with smallest Miles
Type L to search for MonthName with Largest Miles
Type E to exit.
• • If the user types P.
o Populate all the arrays.
• • If the user types S then:
o Ask the user for the Month Name.
o Search the array for that Month Name and find its position in the Monthname array.
o Display the MonthName$, and MilesDriven at the position found during the above search.
• • If the user types M then:
o Search the array for the smallest miles in MilesDriven array and find its position.
o Display the MonthName$, and MilesDriven at the position found during the above search.
• • If the user types L then:
o Search the array for the largest Miles in MilesDriven array and find its position.
o Display the MonthName$, and MilesDriven at the position found during the above search.
• If the user types E. then:
o Terminate the program.
• If the user types any other option:
o Display the message “Invalid Choice. Try again” and go back and display the menu.
PS: You program must keep displaying the menu until the user types the option E, to exit the program."
This is the code I have so far.
Dim MonthNames$(12)
MonthNames$(1) = "January"
MonthNames$(2) = "Febuary"
MonthNames$(3) = "March"
MonthNames$(4) = "April"
MonthNames$(5) = "May"
MonthNames$(6) = "June"
MonthNames$(7) = "July"
MonthNames$(8) = "August"
MonthNames$(9) = "September"
MonthNames$(10) = "October"
MonthNames$(11) = "November"
MonthNames$(12) = "December"
Dim MilesDriven(12)
Search=Findmonth()
E=0
While E = 0
Print "Press P to populate miles and month name"
Print "Press S to search for Month"
Print "Press M to search for Month name with smallest Miles"
Print "Press L to search for MonthName with Largest Miles"
Print "Press E to exit"
Input Answer$
Select Case Answer$
Case "P", "p"
For position = 1 to 12
Print "Enter the amount of miles driven in "; MonthNames$(position)
Input MilesDriven(position)
Next
Case "S", "s"
Function Findmonth()
Print “Please enter a month you want to search for”
Input Month$
For position = 1 to 12
If (Month$ = MonthName$(position)) then
Print "You have driven "; MilesDriven(position); " "; "in the month of " MonthNames$(position)
Exit for
End if
Next
If (position > 12) then
Print “Please enter a valid month”
End if
End function
Case "M", "m"
Case "L", "l"
Case "E", "e"
E=1
Case Else
Print "You made a wrong selection. Please enter again"
End Select
Wend
For position = 1 to 12
Print MonthNames$(position)
Print MilesDriven(position)
Next
Print "Goodbye"
End
There is no need to populate the miles into the month array, rather you have two arrays side by side, such that Monthnames$(i) is the month name and MilesDriven(i) is the miles for that month.
Note that, reading your assignment's instructions, this "populating" should only happen when the user types "P", so move the initialising of your arrays into your Select statement like this:
Dim Monthnames$(12)
Dim MilesDriven(12)
E = 0
While E = 0
Print "Press P to populate miles and month name"
Print "Press S to search for Month"
Print "Press M to search for Month name with smallest Miles"
Print "Press L to search for MonthName with Largest Miles"
Print "Press E to exit"
Input Answer$
Select Case Answer$
Case "P", "p"
Monthnames$(1) = "January"
Monthnames$(2) = "February"
Monthnames$(3) = "March"
Monthnames$(4) = "April"
Monthnames$(5) = "May"
Monthnames$(6) = "June"
Monthnames$(7) = "July"
Monthnames$(8) = "August"
Monthnames$(9) = "September"
Monthnames$(10) = "October"
Monthnames$(11) = "November"
Monthnames$(12) = "December"
MilesDriven(1) = 10
MilesDriven(2) = 20
MilesDriven(3) = 30
MilesDriven(4) = 40
MilesDriven(5) = 50
MilesDriven(6) = 60
MilesDriven(7) = 70
MilesDriven(8) = 80
MilesDriven(9) = 90
MilesDriven(10) = 100
MilesDriven(11) = 110
MilesDriven(12) = 120
Case "S", "s"
Rem - put search by name here
Case "M", "m"
Rem - put search by for smallest miles here
Case "L", "l"
Rem - put search by for largest miles here
Case "E", "e"
E=1
Case Else
Print "You made a wrong selection. Please enter again"
End Select
Wend
Print "Goodbye"
End

Resources