Swift live text field checking for characters from multiple arrays [duplicate] - arrays

This question already has answers here:
Check password string strength criteria in Swift
(2 answers)
Closed 6 years ago.
I have a text field which will serve as a password input. I have created 4 arrays
upperCaseAll = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
lowerCaseAll = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
numberAll = [1 ,2, 3, 4, 5, 6, 7, 8, 9, 0]
specialCharAll = ["-", "/", ":", ";", "(", ")", "$", "&", "#", "\"", ".", ",", "?", "!", "'", "[", "]", "{", "}", "#", "%", "^", "\\", "|", "~", "<", ">", "€", "£", "¥", "•", ".", ","]
I am trying to make the user type at least one of each type of character.
I have 4 text views that will turn from red to green once each rule is met.
Then when all rules are met the red text views turn green and the login button is enabled
My question is how do I live check the text field to see if it has satisfied a rule? I imagine it would have to check each of the arrays overtime a character is entered
Oh also should the numbers be Ints or Strings?
Thanks

It's easier if you use Set instead of Array. Here's an example:
private let upperCaseAll = Set("ABCDEFGHIJKLMNOPQRSTUVWXYZ".characters)
private let lowerCaseAll = Set("abcdefghijklmnopqrstuvwxyz".characters)
private let numberAll = Set("0123456789".characters)
private let specialCharAll = Set("-/:;()$&#\".,?!'[]{}#%^\\|~<>€£¥".characters)
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
// Determines what the new value of the text field will be
let newText = range.length == 0 ? textField.text! + string : (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
// Turn that string into a Set of Characters
let characters = Set(newText.characters)
let hasUpperCase = !characters.intersect(self.upperCaseAll).isEmpty
let hasLowerCase = !characters.intersect(self.lowerCaseAll).isEmpty
let hasNumber = !characters.intersect(self.numberAll).isEmpty
let hasSpecialChar = !characters.intersect(self.specialCharAll).isEmpty
// Now turn your 4 other views to green/red as needed
print("hasUpperCase = \(hasUpperCase), hasLowercase = \(hasLowerCase), hasNumber = \(hasNumber), hasSpecialChar = \(hasSpecialChar)")
...
return true
}

You can do it using the UITextFieldDelegate
See Apple Docs - textField(_:shouldChangeCharactersInRange:replacementString:)
The text field calls this method whenever user actions cause its text
to change. Use this method to validate text as it is typed by the
user. For example, you could use this method to prevent the user from
entering anything but numerical values.
But you should think about using a regex to check the password string. That would be more convenient.

Related

How to get multiple elements from an Array?

I'm using discord.js and I want to create a "server" with passcode. I want the passcode to be 6 letters long, so far I can only do one.
It has to be random as well. Like "ZSHWJK" Instead of "AAAAAA"
Code:
var serverPasscode = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
module.exports = {
name: "createserver",
description: "Creates a server",
run(message, args, client){
const newServer = new MessageEmbed()
.setTitle(`${message.author.name}'s server`)
.setFooter(`${serverPasscode[Math.floor(Math.random() * serverPasscode.length)]}`)
message.channel.send(newServer);
}
};
Right now this can only return one element which is not what I want.
You could use a for loop and create a function such as this:
function makeGuildPassword(length) {
var result = ''; // create empty string that you will add random characters to
var characters = // list of characters (you can change this however you want)
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (var i = 0; i < length; i++) { // create a loop
// add a random character to the string; restart the loop
result += characters.charAt(Math.floor(Math.random() * characters.length));
}
return result;
};
console.log(makeGuildPassword(6))

filter String with empty String in if statement, iOS

I'm working on a custom keyboard. I'm having trouble filtering Strings Array. I want to filter the array only in if statement, I have an array with letters and empty String's. I want to filter Array only in if statement because I want to remove last character, not empty String.
Here is my code:
keyboardKeyStr = ["h", "e", "l", "l", "o", " ", "", "t", "h", "i", "s", "", "i", "s", " ", "m", "y", " ", "", "a", "r", "r", "a", "y", " "]
if range.length == 1 && text.count == 0 {
print("Delete Button")
let emptyStr: String? = ""
keyboardKeyStr = keyboardKeyStr.filter {$0 != emptyStr}
keyboardKeyStr.removeLast()
keyboardKeyArr.append(keyboardView.deleteBtn)
self.keyboardKeyStr.append("")
}
Try this keyboardKeyStr = keyboardKeyStr.filter{ $0 != "" && $0 != " "}

How to generate random string form array of elements by compared with another array of elements in swift?

I aware to get random character from a string. From here is the code,
func randomString(_ length: Int) -> String {
let master = Array("abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ_123456789".characters) //0...62 = 63
var randomString = ""
for _ in 1...length{
let random = arc4random_uniform(UInt32(master.count))
randomString.append(String(master[Int(random)]))
}
return randomString
}
Usage :
var randomArray1 = [String]()
for _ in 0...62{
randomArray1.append(self.randomString(1))
}
Here, If randomArray1.append(self.randomString(x)), then x = 1...N
Checking repeated elements :
var sameElementCatcher = [String]()
for x in 0...62{
let element = randomArray1[x]
randomArray1[x] = ""
if randomArray1.contains(element){
sameElementCatcher.append(element)
}
}
print("Same Elements")
print(sameElementCatcher.count != 0 ? sameElementCatcher : "Array count is zero")
Output:
Same Elements
["_", "u", "8", "7", "E", "P", "u", "y", "C", "-", "C", "x", "l", "j",
"t", "D", "U", "2", "e", "2"]
But I need to get array of 62 unique random characters from master by compared with randomArray1. i.e., Array count is zero
How can I achieve this without delay?
Note:
Also, I read this post also I have a answer for shuffling array. But this post different from shuffling only, Please, see usage.
Did you try like this?
What I understand from your question. generate a random text where all the characters are unique.
Before appending your random string to your array check is array have that char then append into your array.
func randomString(_ length: Int) -> String {
let master = Array("abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ_123456789".characters) //0...62 = 63
var randomString = ""
for _ in 1...length{
let random = arc4random_uniform(UInt32(master.count))
randomString.append(String(master[Int(random)]))
}
return randomString
}
var randomArray1 = [String]()
var tempRandomString = ""
let totalRandomCount = 62
var randomArrayCount = 0
while (totalRandomCount > randomArrayCount) {
tempRandomString = randomString(1)
if !randomArray1.contains(tempRandomString) {
randomArray1.append(tempRandomString)
randomArrayCount+=1
}
}
print(randomArray1)
Output: ["X", "u", "j", "1", "n", "E", "D", "q", "U", "6", "T", "O", "f", "J", "i", "c", "W", "V", "G", "R", "k", "7", "_", "8", "-", "l", "w", "4", "e", "Q", "C", "m", "M", "Y", "o", "S", "B", "2", "Z", "P", "p", "N", "y", "H", "a", "h", "z", "s", "b", "A", "3", "g", "x", "L", "v", "F", "d", "r", "t", "K", "9", "5"]
I tried this with playground. For this output 199 times loop executed.
If anyone knows better than this update yours.

Iterate through array and start a new array whenever a certain value is found

Say i have the following array:
array = ["a", "b", "c", "new", "d", "e", "f", "g", "new", "h", "i", "new"]
I want to create a new array every time I find the value "new".
So I would end up with:
array1 = ["a", "b", "c"]
array2 = ["d", "e", "f", "g"]
array3 = ["h", "i"]
What would be the best way to do this?
As the name implies you can split arrays with the split function, however that returns ArraySlice objects.
To get arrays back from the slices you need to map them (credits to Martin R)
let array = ["a", "b", "c", "new", "d", "e", "f", "g", "new", "h", "i", "new"]
let splittedArrays = array.split(separator: "new").map(Array.init)

How to "or" all elements in the range of an array

I am trying to say if x == consonants[0] or [1] or [2] all the way to [21] on one line. For some reason I thought consonants[0..21] would work but it doesn't:
consonants = ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t",
"v", "w", "x", "y", "z"]
new_first = ["m", "a", "t", "t", "h", "e", "w", "s"]
new_first.each do |x|
if x == consonants[0]
puts x.next!
elsif x == consonants[1]
puts x.next!
elsif x == consonants[2]
puts x.next!
elsif x == consonants[3]
puts x.next!
else
puts "test failed"
end
end
There's several ways to crack this nut, but it depends on your performance concerns, and how extensible this needs to be. Normally a chain of if statements which are of the form x == y and x == z can be folded into:
case (x)
when y, z
# ... Executed on a match
end
In your case you can even do this by using your array as a list of valid values:
case (x)
when *constants
puts x.next!
end
For larger lists you might want to fold this up into a Set since these are optimized for include? tests:
consonants_set = Set.new(consonants)
if (consonants_set.include?(x))
puts x.next!
end
Since you're doing single letter matches you have a lot more options. For example, a regular expression:
consonants_regexp = Regexp.new('[%s]' % consonants.join)
if (consonants_regexp.match(x))
puts x.next!
end
Or you can even do a simple substring match:
consonants_string = consonants.join
if (consonants_string[x])
puts x.next!
end
Worth noting but you can iterate over the characters in strings:
'cwzbrly'.each_char do |c|
puts c
end
That avoids the need to create and/or type in long arrays of the form [ 'a', 'b', ... ].
You can do this:
if consonants.include? (x)
your-code-here
end
This will check if there's an element equal to x inside the array.
You can use regular expression
if x =~ /[aeiou]/
puts 'test failed'
else
puts x.next!
end
/[aeiou]/ is saying match anything that is a, e, i, o, or u.
This will eliminate the need to create an array of consonants.
If I correctly understand and you're expecting nuuixt as a result, you can do:
new_first.select { |letter| consonants.include?(letter) && letter.next! }
&& works here this way: if consonants.include?(letter) evaluates to true then block return letter.next! .
Consider this:
consonants = ('a' .. 'z').to_a - %w[a e i o u] # => ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z"]
shifted_consonants = consonants.zip(consonants.rotate).to_h # => {"b"=>"c", "c"=>"d", "d"=>"f", "f"=>"g", "g"=>"h", "h"=>"j", "j"=>"k", "k"=>"l", "l"=>"m", "m"=>"n", "n"=>"p", "p"=>"q", "q"=>"r", "r"=>"s", "s"=>"t", "t"=>"v", "v"=>"w", "w"=>"x", "x"=>"y", "y"=>"z", "z"=>"b"}
'matthews'.chars.map{ |c| shifted_consonants[c] || c } # => ["n", "a", "v", "v", "j", "e", "x", "t"]
That took the range of 'a' to 'z' and converted it to an array, then subtracted the array of vowels, resulting in only consonants.
Next, it turned the array of consonants into a hash/look-up table shifted_consonants where each key is a current consonant and the value is the next consonant.
Finally, it takes each character in 'matthews' and looks to see if there is a value in shifted_consonants for that character. If not, nil is returned, which triggers || and returns the current character. If there is a hit in the hash, the next value for that consonant is returned, which short-circuits the || "or".
An alternate would be to take advantage of tr:
consonants = (('a' .. 'z').to_a - %w[a e i o u]).join # => "bcdfghjklmnpqrstvwxyz"
shifted_consonants = consonants.chars.rotate.join # => "cdfghjklmnpqrstvwxyzb"
'matthews'.tr(consonants, shifted_consonants).chars # => ["n", "a", "v", "v", "j", "e", "x", "t"]
Checking for speed:
CONSONANTS = ('a' .. 'z').to_a - %w[a e i o u] # => ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z"]
SHIFTED_CONSONANTS_HASH = CONSONANTS.zip(CONSONANTS.rotate).to_h # => {"b"=>"c", "c"=>"d", "d"=>"f", "f"=>"g", "g"=>"h", "h"=>"j", "j"=>"k", "k"=>"l", "l"=>"m", "m"=>"n", "n"=>"p", "p"=>"q", "q"=>"r", "r"=>"s", "s"=>"t", "t"=>"v", "v"=>"w", "w"=>"x", "x"=>"y", "y"=>"z", "z"=>"b"}
CONSONANTS_STR = (('a' .. 'z').to_a - %w[a e i o u]).join # => "bcdfghjklmnpqrstvwxyz"
SHIFTED_CONSONANTS_STR = CONSONANTS_STR.chars.rotate.join # => "cdfghjklmnpqrstvwxyzb"
require 'fruity'
sample_string = 'matthews'
compare do
use_hash { sample_string.chars.map{ |c| SHIFTED_CONSONANTS_HASH[c] || c } }
use_tr { sample_string.tr(CONSONANTS_STR, SHIFTED_CONSONANTS_STR).chars }
end
# >> Running each test 2048 times. Test will take about 1 second.
# >> use_tr is faster than use_hash by 10.000000000000009% ± 10.0%
The longer the sample string is, the greater the difference. Changing to:
sample_string = 'matthews' * 1000
I see a result of:
# >> Running each test 4 times. Test will take about 1 second.
# >> use_tr is faster than use_hash by 4x ± 0.1
Found in a comment, NOT in the question where it belongs...
my goal is to change the constonant to the next consonant and the vowel to the next vowel for the arraynew_first = ["m", "a",
Adjusting for that here's some changes. You can unravel the deltas:
ALPHABET = ('a' .. 'z').to_a
VOWELS = %w[a e i o u]
CONSONANTS = ALPHABET - VOWELS # => ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z"]
SHIFTED_CONSONANTS = CONSONANTS.rotate # => ["c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z", "b"]
SHIFTED_VOWELS = VOWELS.rotate # => ["e", "i", "o", "u", "a"]
SHIFTED_CONSONANTS_HASH = CONSONANTS.zip(SHIFTED_CONSONANTS).to_h # => {"b"=>"c", "c"=>"d", "d"=>"f", "f"=>"g", "g"=>"h", "h"=>"j", "j"=>"k", "k"=>"l", "l"=>"m", "m"=>"n", "n"=>"p", "p"=>"q", "q"=>"r", "r"=>"s", "s"=>"t", "t"=>"v", "v"=>"w", "w"=>"x", "x"=>"y", "y"=>"z", "z"=>"b"}
SHIFTED_VOWELS_HASH = VOWELS.zip(SHIFTED_VOWELS).to_h # => {"a"=>"e", "e"=>"i", "i"=>"o", "o"=>"u", "u"=>"a"}
sample_string = 'matthews'
sample_string.chars.map{ |c| SHIFTED_CONSONANTS_HASH[c] || SHIFTED_VOWELS_HASH[c] } # => ["n", "e", "v", "v", "j", "i", "x", "t"]
CONSONANTS_STR = CONSONANTS.join # => "bcdfghjklmnpqrstvwxyz"
SHIFTED_CONSONANTS_STR = SHIFTED_CONSONANTS.join # => "cdfghjklmnpqrstvwxyzb"
SHIFTED_VOWELS_STR = SHIFTED_VOWELS.join # => "eioua"
CHARACTERS_STR = (CONSONANTS + VOWELS).join # => "bcdfghjklmnpqrstvwxyzaeiou"
SHIFTED_CHARACTERS_STR = SHIFTED_CONSONANTS_STR + SHIFTED_VOWELS_STR # => "cdfghjklmnpqrstvwxyzbeioua"
sample_string.tr(CHARACTERS_STR, SHIFTED_CHARACTERS_STR).chars # => ["n", "e", "v", "v", "j", "i", "x", "t"]
The changes won't affect the speed of the actual code: tr would still outrun using the hash lookups.

Resources