1

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 ?

3
  • What is the error? DId you try printing var a, what is the output? Commented Feb 20, 2020 at 14:31
  • Is alphabet intentionally an Array<String> rather than an Array<Character>? Commented Feb 20, 2020 at 15:01
  • 1
    I nominated the question for re-opening because the linked question this is supposed to be a duplicate of was actually about a different problem, although the overall task in which it occurred was the same Commented Feb 20, 2020 at 15:27

6 Answers 6

4

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()! })
Sign up to request clarification or add additional context in comments.

9 Comments

Just note that OP's alphabet is an array of strings, not of characters.So you probably need to join them.
@MartinR flatMap does the trick: String((0 ..< 6).flatMap { _ in alphabet.randomElement()! })
@JeremyP Or (0 ..< 6).map { _ in alphabet.randomElement()! }.joined(). As I said before: “Swift is the new Perl – TIMTOWTDI!”
@LeoDabus: Jeremy edited his comment, my response does not apply anymore (I have deleted it now).
@Alexander-ReinstateMonica Great. I took the liberty of editing a missed occurrence of Character but also upvoted. Also, in the circumstances, you don't need to convert the string "abcdefghijklmnopqrstuvwxyz" to an array because you can apply .randomElement() directly to a string.
|
1

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.

5 Comments

Thank you very much,, it works !!! But I would like to understand why, what is the rule with "!" character ? When do you need to put one ?
@SamAm You should read up on Optional, an important concept in swift
Thank you very much for you help, guys
@SamAm Concatenation operation deals with non-optional values. So, you need to unwrap any optional values before using + operation. For unwrapping you either using ! or if-let statement. Do read more about optionals.
1

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

Comments

1

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)

1 Comment

Thanks a lot, but far too complicated for me at the moment ^^
1

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.

Comments

0

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() ?? "")
}

3 Comments

This is nice but I just can't condone teaching a beginner to use !. I suggest result + (alphabet.randomElement() ?? "") as in my answer.
@matt Updated that. Thanks.
The only case where randomElement() returns nil is for an empty collection. If the alphabet is a non-empty array (known at compile time) then I do not see a problem in force-unwrapping.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.