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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Programming Challenge: Are You a Swift Ninja? Part 1
25 mins
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 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 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 — 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 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.
-
For an extra shuriken – write the function as a single
return
statement and don’t use any loops (i.e. nofor
orwhile
).
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"
[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
to process the elements of the array one by one. You can use Array
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!
[/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
to convert all elements of the array to their Int
value (as per the requirements), and then use Array
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:
- Matches an empty string “” or a 0 and converts such elements to the value -10.
- Matches a string element, which when converted to
Int
is greater than 0 – converts such elements to theirInt
value. - Matches an
Int
and returns its value. - 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
. 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 toInt[]
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.
[/spoiler]