Reputation: 39
I'm training on Swift, doing some exercises with arrays. I would like to pick up randomly 6 letters in an array containing the 26 letters of the alphabet, then concatenate them to generate a string.
I have no problem picking the values, but when it comes to concatenate them, I get error messages.
Here is my code :
var alphabet = ["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"]
var a = alphabet.randomElement()
var b = alphabet.randomElement()
var c = alphabet.randomElement()
var d = alphabet.randomElement()
var e = alphabet.randomElement()
var f = alphabet.randomElement()
var password = a + b + c + d + e + f
print(password)
What am I doing wrong ?
Upvotes: 1
Views: 787
Reputation: 143
As others already mentioned, randomElement() can return nil as the reference says:
An easy solution will be like the following.
let password = alphabet[Int.random(in: 0...25)]
+ alphabet[Int.random(in: 0...25)]
+ alphabet[Int.random(in: 0...25)]
+ alphabet[Int.random(in: 0...25)]
+ alphabet[Int.random(in: 0...25)]
+ alphabet[Int.random(in: 0...25)]
In this way, basically you tell swift that you want to handle the array out of range error.
Upvotes: 1
Reputation: 78
you can try this method
func randomPassword(digit : Int) -> String {
var password = ""
for _ in 0 ..< digit {
let number = Int.random(in: 97...122)
password += "\(UnicodeScalar(number)!)"
}
return password.uppercased()
}
let password = randomPassword(digit: 6)
print(password)
Upvotes: 1
Reputation: 63399
Your alphabet is currently an [String]
(a.k.a. Array<String>
). You should probably change it to an [Character]
instead.
Even better, since randomElement
is defined on Collection
, and Collection
whose Element
is Character
would work. String
is a Collection
, and its Element
is Character
, so it fits the bill perfectly. The nicest way to do that is by breaking apart a string literal:
let alphabet = "abcdefghijklmnopqrstuvwxyz"
randomElement()
returns a T?
(in this case, String?
, a.k.a. Optional<String>
), not just a T
. Because if alphabet
were to be empty (it's not, but if it was), then there would be no elements to return. The only sane thing to do is return nil
instead.
Since in your case you can be certain that alphabet
is non-empty, and that randomElement()
will always return a valid non element, you're justified to force unwrap the result with the !
operator. Now don't make a bad habit of this. You have good reason to force unwrap here, but it's not to be used as a first-resort method of optional handling.
You're also repeating yourself a lot. You can improve this by using a loop of some kind or another.
In this case, you can use a neat little trick. Starting with a range like 0..<6
, which has 6 elements, you can call map
on it to transform its elements (which are 0
, 1
, ... 5
). In this case, you woudl transform each element by ignoring it, and just replacing it with a random element frmo the alphabet:
(0..<6).map { _ in alphabet.randomElement()! }
The result is an Array<Character>
. All you need is to turn it into a string. Final code:
let alphabet = "abcdefghijklmnopqrstuvwxyz"
let password = String((0..<6).map { _ in alphabet.randomElement()! })
Upvotes: 4
Reputation: 24341
Instead of creating all these a, b, c, d, e, f
variables, you can simply use reduce(_:_:)
to get the expected result.
let password = (1...6).reduce("") { (result, _) -> String in
return result + (alphabet.randomElement() ?? "")
}
Upvotes: 0
Reputation: 535989
Actually I think the simplest solution is to declare an extension at the top level of your file, allowing you to concatenate Optional Strings coherently:
func +(lhs:String?, rhs:String?) -> String {
return (lhs ?? "") + (rhs ?? "")
}
Now your original code will compile and run correctly without changes (except that I changed var
to let
everywhere to eliminate compiler warnings):
let alphabet = ["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"]
let a = alphabet.randomElement()
let b = alphabet.randomElement()
let c = alphabet.randomElement()
let d = alphabet.randomElement()
let e = alphabet.randomElement()
let f = alphabet.randomElement()
let password = a + b + c + d + e + f
print(password) // cpndmz
Upvotes: 1
Reputation: 24341
a, b, c, d, e, f
are of type String?
. So you need to unwrap them either using if-let
or force-unwrapping before, i.e.
Using force-unwrapping,
let a = alphabet.randomElement()!
let b = alphabet.randomElement()!
let c = alphabet.randomElement()!
let d = alphabet.randomElement()!
let e = alphabet.randomElement()!
let f = alphabet.randomElement()!
Using if-let
,
if let a = alphabet.randomElement(), let b = alphabet.randomElement(), let c = alphabet.randomElement(), let d = alphabet.randomElement(), let e = alphabet.randomElement(), let f = alphabet.randomElement() {
let password = a + b + c + d + e + f
print(password)
}
Using if-let is safer than force-unwrapping.
Note: Use let
instead of var
when using constant values.
Upvotes: 1