3
\$\begingroup\$

Task: Write a function, which receives an optional array of optional integers and returns a none-optional integer. In case the array isn't nil: Return one randomly selected integer. In case the array is nil: Return a random number between 1 through 100. In case a selected integer in nil: Return a random number between 1 through 100.

The body of the function has to be a single-line of code.

My solution:

func getRandomInt(outOf oArr: [Int?]?) -> Int {
    (oArr?.randomElement() ?? Int.random(in: 1...100)) ?? Int.random(in: 1...100)
}

print(getRandomInt(outOf: nil))
print(getRandomInt(outOf: [1, 2, 3, 4, 5, 6, 7, 8]))
print(getRandomInt(outOf: [1, 2, nil, 4, nil, 6, 7, nil, 9]))

Is there a better way to solve the task?

What's your opinion concerning my naming and the usage of the argument-label/parameter-name? Is it alright to prefix the function with "get", concerning the given context? Or is there something more appropriate?

\$\endgroup\$
1
  • 1
    \$\begingroup\$ The repetition of Int.random(in: 1...100)) feels clunky. Consider oArr?.randomElement().flatMap { $0 } ?? Int.random(in: 1...100). Adjust the naming of the function and the argument label to make the code better readable. \$\endgroup\$
    – ielyamani
    Commented Oct 12 at 20:49

1 Answer 1

3
\$\begingroup\$

Naming this function is a bit difficult because this is a (contrived) programming task. My suggestion would be something like

func randomInt(in optionalInts: [Int?]?) -> Int

for the following reasons:

  • Swift “getter” functions usually do not have a “get” prefix in their name.
  • in is used as an argument label in similar functions, like the random(in:) method.
  • Abbreviations like oArr should be avoided.

For a real application one could surely find a better name from the context.

“The body of the function has to be a single-line of code” is actually a bad requirement. It is meaningless, since one can always put arbitrary code in a single line of Swift. What is perhaps meant is that the body has to be a single expression. That is possible (as you demonstrated), but it decreases the legibility of the program.

I'll ignore that requirement for a moment and come back to it at the end.

Optional chaining with

optionalInts?.randomElement()

is a good start. That expression evaluates to a “double optional” of type Optional<Optional<Int>> or Int?? for short. The following cases are possible:

  1. If optionalInts is nil then the above expression evaluates to .none.
  2. If optionalInts is not nil but randomElement() selects nil from the array then the expression evaluates to .some(.none). The same happens if the array is empty.
  3. Otherwise the expression evaluates to .some(.some(x)) where x is an element of the array.

In the last case the function should return x, on all other cases it should return some random integer in the range 0...100.

The Optional Pattern can be used to test if a value can be unwrapped twice, either with a switch statement

func randomInt(in optionalInts: [Int?]?) -> Int {
    switch optionalInts?.randomElement() {
    case let element??:
        element
    default:
        Int.random(in: 1...100)
    }
}

or with a if case let expression:

func randomInt(in optionalInts: [Int?]?) -> Int {
    if case let element?? = optionalInts?.randomElement() {
        element
    } else {
        Int.random(in: 1...100)
    }
}

Note that we need Int.random only once, and not twice as in your solution.

If you add a function description then this becomes fairly readable.

It is possible to achieve the same goal with a single expression and two nil-coalescing operators, each of which decreases the optionality by one:

func randomInt(in optionalInts: [Int?]?) -> Int {
    return (optionalInts?.randomElement() ?? nil) ?? Int.random(in: 1...100)
}

This is almost what you did, but still avoids the repetition of Int.random. Here the expression

(optionalInts?.randomElement() ?? nil)

evaluates to a “simple optional” Int?, which is .none in the cases 1 and 2 above, and .some(x) in case 3.

\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.