Programming Challenge: Are You a Swift Ninja? Part 1

Do you consider yourself a Swift Ninja? Take our programming challenge! Beginners are also welcome to follow through and learn the craft. By Marin Todorov.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

Challenge #2

Swift functions are very flexible — they can take a variable number of arguments, return one or more values, return other functions and much more.

For this challenge you’ll test your understanding of function syntax in Swift. Write a function called flexStrings that meets the following requirements:

  • The function can take precisely 0, 1 or 2 string parameters.
  • Returns the function parameters concatenated as String.
  • If no parameters pass to the function, it will return the string “none”.
  • For an extra shuriken extrashuriken use one line of code for the function body.

Here’s some example usage and output of the function:

flexStrings() //--> "none"
flexStrings(s1: "One") //--> "One"
flexStrings(s1: "One", s2: "Two") //--> "OneTwo"

Take 3shurikens for solving the problem, and an extra shuriken for a total of 4 if you did it in one line.

[spoiler title=”Hints”]
Swift function parameters can have default values, and because of this you can omit it when calling the function.

Remember for this challenge you can now give yourself only 2shurikens — no extra shuriken for a one line solution either!
[/spoiler]

[spoiler title=”Tutorial”]
Swift function parameters can have a default value, and that is one of the differences between good old Objective-C and Swift. When a parameter has a default value, you call it by name when invoking the function.

The benefit is that you can omit the parameter if you’d like to use that default value. Nice!

As for the solution? It’s simple when you know about default parameter values:

func flexStrings(s1: String = "", s2: String = "") -> String {
    return s1 + s2 == "" ? "none": s1 + s2
}

You define a function that takes two parameters, but both of them have a default value of “” (an empty string). This way you can call the function with:

  • 2 parameters as normal.
  • 1 parameter, provided by you, and the 2nd parameter having the default value “”.
  • No parameters so the function will use the default values for both.

So all you do in the function body is to check if both parameters are empty strings (s1+s2 == "") and if so return “none”, otherwise just concatenate s1 and s2 and return the result. Voila! All requirements met.

To meet the one-line requirement, you can see that the ternary operator from Objective-C ?: has made an encore in Swift.

Try calling this function with different parameters inside a Playground — make sure you understand how it works. Give yourself 1shuriken for doing that.

[/spoiler]

Challenge #3

You’ve already mastered functions with optional parameters in the previous challenge. That was fun!

But with the approach from the previous solution, you can only have a fixed maximum number of parameters. There’s a better approach if you truly want a variable number of input values for a function.

This challenge demonstrates how to best use the built-in Array methods and switch statements. Did you pay attention when you read Apple’s Swift Programming Language book? You’re about to find out. :]

Write a function called sumAny that can take 0 or more parameters of any type. The function should meet the following requirements:

  • The function will return the sum of the passed parameters as a String, following the rules below.
  • If a parameter is an empty string or an Int equal to 0, add -10 to the result.
  • If a parameter is an String that represents a positive number (e.g. “10”, not “-5”), add it to the result.
  • If a parameter is an Int, add it to the result.
  • In any other case, do not add it to the result.
  • extrashuriken For an extra shuriken – write the function as a single return statement and don’t use any loops (i.e. no for or while).

Here’s some example calls to the function with their output, so you can check your solution:

let resultEmpty = sumAny() //--> "0"
let result1 = sumAny(Double(), 10, "-10", 2) //--> "12"
let result2 = sumAny("Marin Todorov", 2, 22, "-3", "10", "", 0, 33, -5) //--> "42"

3shurikens

[spoiler title=”Hints”]
You define a function that takes variable number of parameters by defining its last parameter to be name: Type.... Then, from the function body you can access name as a normal Array.

You can use Array.map((T)->(T)) to process the elements of the array one by one. You can use Array.reduce(T, (T)->(T)) to reduce an array of values to a single value.

Finally, calculate the sum as a number and just cast it to a String before returning the result. Good luck!

2shurikens

[/spoiler]

[spoiler title=”Tutorial”]

This problem uses quite a bit of Swift’s built-in language features, so let’s explore a few separate concepts before approaching the final solution.

First look at how to define a function that takes in a variable number of parameters:

func sumAny(anys: Any...) -> String {
  //code
}

If you put “…” after the type of a function parameter, Swift will expect 0 or more values to pass to the function of that type. When you specify Any... that means that any number of any type of values can pass to the function.

You can treat the anys parameter from the example above as a normal Array in your function. For example:

func sumAny(anys: Any...) -> String {
  return String(anys.count)
}

The code above returns the count of elements in the array anys, and it’s also the number of parameters passed to the function.

Next, you need to get the sum of the values. You could loop over the elements of the array and use a separate variable to hold the sum, but that solution won’t give you that extra shuriken :]

You’ll employee a different strategy — use Array.map((T)->(T)) to convert all elements of the array to their Int value (as per the requirements), and then use Array.reduce(T, (T)->(T)) to sum all values together.

Here’s the code that converts each element in the array to their sum value:

anys.map({item in
  switch item {
    case "" as String, 0 as Int:
      return -10
    case let s as String where s.toInt() > 0:
      return s.toInt()!
    case is Int:
      return item as Int
    default:
      return 0
  }
})

map iterates over every element in the array and process it using the given closure. Altering the element and/or returning any value you want is an options. In the closure above, you just do a single switch statement and convert every element to its value as specified in the requirements.

If you’re not familiar with advanced switch cases in Swift, here’s what each of the cases does:

  1. Matches an empty string “” or a 0 and converts such elements to the value -10.
  2. Matches a string element, which when converted to Int is greater than 0 – converts such elements to their Int value.
  3. Matches an Int and returns its value.
  4. All other elements in the array that don’t match any of the cases above will be converted to the value of 0.

Great! This switch statement converts your random values to only integers. After you call map, you effectively have an array of Int values instead of an array of Any values.

Finding the sum of array elements is a perfect application for Array.reduce(T, (T)->(T)). Let’s see how that function works by considering this example:

[1,2,3].reduce(4) { result, item in
    return result + item
} //--> 10
  • You declare an Array with the elements 1, 2, and 3.
  • Then you call reduce with starting value of 4.
  • reduce calls the given closure with 4 (result) and the first element 1 (item). The closure then produces the result 5.
  • Next, reduce calls the closure with the result 5 (the previous result) and next item in the array — 2.
  • The result this time is 7, so when reduce goes over the last array element and adds 3 – the final result becomes 10.

reduce is a very handy function — you avoid needing to declare arrays or variables, and the code looks much cleaner.

Now combine all techniques discussed above to produce the final solution:

func sumAny(anys: Any...) -> String {
    return String((anys.map({item in
        switch item {
        case "" as String, 0 as Int:
            return -10
        case let s as String where s.toInt() > 0:
            return s.toInt()!
        case is Int:
            return item as Int
        default:
            return 0
        }
        }) as [Int]).reduce(0) {
            $0 + $1
        })
}

How this works, point by point:

  • You use map as discussed to convert all elements to their sum values.
  • You cast map‘s result to Int[] since all elements are integers.
  • You use reduce as in the example earlier to sum the array elements.
  • Finally, you cast the result to a String before returning.

You did not need to use any loops, nor any variables — just the built-in Array functions. How cool is that?

If you were able to learn and understand how to use map and reduce, give yourself 1 shuriken.

1shuriken

[/spoiler]

Contributors

Over 300 content creators. Join our team.