How to handle initial nil value for reduce functions - swift

I would like to learn and use more functional programming in Swift. So, I've been trying various things in playground. I don't understand Reduce, though. The basic textbook examples work, but I can't get my head around this problem.
I have an array of strings called "toDoItems". I would like to get the longest string in this array. What is the best practice for handling the initial nil value in such cases? I think this probably happens often. I thought of writing a custom function and use it.
func optionalMax(maxSofar: Int?, newElement: Int) -> Int {
if let definiteMaxSofar = maxSofar {
return max(definiteMaxSofar, newElement)
}
return newElement
}
// Just testing - nums is an array of Ints. Works.
var maxValueOfInts = nums.reduce(0) { optionalMax($0, $1) }
// ERROR: cannot invoke 'reduce' with an argument list of type ‘(nil, (_,_)->_)'
var longestOfStrings = toDoItems.reduce(nil) { optionalMax(count($0), count($1)) }

It might just be that Swift does not automatically infer the type of your initial value. Try making it clear by explicitly declaring it:
var longestOfStrings = toDoItems.reduce(nil as Int?) { optionalMax($0, count($1)) }
By the way notice that I do not count on $0 (your accumulator) since it is not a String but an optional Int Int?
Generally to avoid confusion reading the code later, I explicitly label the accumulator as a and the element coming in from the serie as x:
var longestOfStrings = toDoItems.reduce(nil as Int?) { a, x in optionalMax(a, count(x)) }
This way should be clearer than $0 and $1 in code when the accumulator or the single element are used.
Hope this helps

Initialise it with an empty string "" rather than nil. Or you could even initialise it with the first element of the array, but an empty string seems better.

Second go at this after writing some wrong code, this will return the longest string if you are happy with an empty string being returned for an empty array:
toDoItems.reduce("") { count($0) > count($1) ? $0 : $1 }
Or if you want nil, use
toDoItems.reduce(nil as String?) { count($0!) > count($1) ? $0 : $1 }
The problem is that the compiler cannot infer the types you are using for your seed and accumulator closure if you seed with nil, and you also need to get the optional type correct when using the optional string as $0.

Related

Append Values in NSSet To An Array of Type [String]

I am attempting to get the values from an NSSet in core data and append those values to an array of type String.
func addStepsToArray() {
if let steps = entity.steps {
for i in steps {
recipeStep.append(String(i))
}
}
}
entity.steps is the list of steps tied to a core data entity. This is an NSSet. I am trying to copy those values to an array of type String.
#State var recipeStep: [String]
When trying to do this in my for in loop, I receive the following error: No exact matches in call to initializer
If I remove the conversion of "I" to String, I receive the following error:
Cannot convert value of type NSSet.Element (aka Any) to expected argument type String
Any idea on how to get this to work?
NSSet is defined in Objective C, which didn't have generics. It's an untyped collection, so you don't statically know anything about its elements.
As you've noticed, your i variable isn't a String, it's an Any.
You're confusing type coercion ("casting") with type conversion. If i were a Double, you could call String(i) to invoke an initializer which takes a double, and processes into a String.
You tried something similar by calling String(i), where you're making the Swift compiler find an initializer on String with the signitiure init(_: Any).
There is no such initializer. And besides, that's not what you want. You don't want to create a new String from a different kind of value. You already have a string, it's just "hidden" behind an Any reference.
What you're looking for is to do a down-cast, from Any to String:
func addStepsToArray() {
if let steps = entity.steps {
for i in steps {
guard let step = i as? String else {
fatalError("Decide what to do if the value isn't a String.")
}
recipeStep.append(i as String)
}
}
}
I'll warn you though, there are several issues/blemishes with this code:
You're using a for loop to do what is ultimately just a mapping operation
Your computation doesn't return its ouput, and instead indirectly achieves its goal through a side-effect on recipeStep
Your computation doesn't take a its input as a parameter, and instead indirectly achieves its goal through a side-effect on entity
i is conventionally expected to be an integer index of a for loop iterating over a sequence of numbers. Here it's an Any (a String at runtime)
Here's what I would suggest instead:
func getRecipeSteps(from entity: MyEntityType) -> [String] {
guard let steps = entity.steps else { return [] }
return steps.map { step in
guard let stringStep = step as? String else {
fatalError("Decide what to do if the value isn't a String.")
}
return step
}
}
Then in the rest of your code (and your tests), you can write self.recipeSteps = getRecipeSteps(from: myEntity). Elegant!
If you're certain that these entity.steps values can only ever be strings, then you can boil this down to a single map with a force-cast:
func getRecipeSteps(from entity: MyEntityType) -> [String] {
entity.steps?.map { $0 as! String } ?? []
}
Just convert directly:
let set = Set(["1", "2", "3"])
let array = Array(set)
DDLog(set)//Set<String>)
DDLog(array)//[String]

Check if string contains optional string in Swift, but only if it's not nil

I'd like to add an optional "filter" parameter to a function that processes a list of strings. If the filter is nil, I'd like to process all the strings, otherwise I'd only like to process the ones that contain the filter. Roughly:
func process(items: [String], filter: String?) {
for item in items {
if filter == nil || item.contains(filter) {
// Do something with item
}
}
}
The typechecker complains about passing filter into contains since it's optional, but contains takes a String. I could of course force-unwrap, but that seems ugly. The above would compile in Kotlin because it would smart-cast away the optional, but what's the idiomatic way to express this in Swift?
Thanks!
There is a map on Optional that executes the provided closure only if the optional is not nil. You can use map along with the nil coalescing operator ?? to provide the default value of true if the map returns nil because the filter is nil:
func process(items: [String], filter: String?) {
for item in items {
if filter.map(item.contains) ?? true {
// process item
}
}
}
The key here is (and always was going to be) the nil-coalescing operator. This lets you unwrap an Optional safely if it is not nil, but if it is nil, you substitute another value.
The issue with the question, however, is that it poses itself in an unSwifty way. You would never write a filtering function that takes an Optional String. You would write a filtering function that takes an Optional filtering function! Instead of limiting the caller to contains and a String, you want to allow any type of array and any filtering function. That is the Swifty way to write this function.
And while we are at it, the function should not predetermine what is done with the members of the array either. That should be another function that the caller passes in!
Thus we end up with this far more general and much Swiftier statement of the goal:
func process<T>(_ arr: [T],
filtering filterPred: ((T)->Bool)? = nil,
processing processPred: ((T)->Void)) {
// ...
}
Okay! So let's write this function. The processing part is easy; this is the predicate to a forEach call. Like this:
func process<T>(_ arr: [T],
filtering filterPred: ((T)->Bool)? = nil,
processing processPred: ((T)->Void)) {
arr.forEach(processPred)
}
Very good! But we have neglected to deal with the filtering function. Let's take care of that. Clearly, this is where the rubber meets the road. And just as clearly, it is evident that this is a function to be passed into filter. But we can only do that by unwrapping if it is not nil. What if it is nil? The OP has specified that in that case we should let all element through the filter.
So consider a predicate function that is to be passed into filter. What is the function that lets everything through? It is:
{ _ in true }
So that is what to put after the nil-coalescing operator! Therefore, this is the full answer:
func process<T>(_ arr: [T],
filtering filterPred: ((T)->Bool)? = nil,
processing processPred: ((T)->Void)) {
arr.filter(filterPred ?? { _ in true }).forEach(processPred)
}
And here are two tests:
let arr = [1,2,3,4]
self.process(arr) { print($0) }
self.process(arr) { $0 % 2 == 0 } processing: { print($0) }

compactMap behaves differently when storing in an optional variable

Consider the following array.
let marks = ["86", "45", "thiry six", "76"]
I've explained my doubts in the following two cases.
Case#1
// Compact map without optionals
let compactMapped: [Int] = marks.compactMap { Int($0) }
print("\(compactMapped)")
Result - [86, 45, 76]
Case#2
// Compact map with optionals
let compactMappedOptional: [Int?] = marks.compactMap { Int($0) }
print("\(compactMappedOptional)")
Result - [Optional(86), Optional(45), nil, Optional(76)]
Why there is "nil" in the result of Case#2? Can anyone explain why is it not like this [Optional(86), Optional(45), Optional(76)] in Case#2? (PFA playground)
I submitted this behavior as a bug at bugs.swift.org, and it came back as "works as intended." I had to give the response some thought in order to find a way to explain it to you; I think this re-expresses it pretty accurately and clearly. Here we go!
To see what's going on here, let's write something like compactMap ourselves. Pretend that compactMap does three things:
Maps the original array through the given transform, which is expected to produce Optionals; in this particular example, it produces Int? elements.
Filters out nils.
Force unwraps the Optionals (safe because there are now no nils).
So here's the "normal" behavior, decomposed into this way of understanding it:
let marks = ["86", "45", "thiry six", "76"]
let result = marks.map { element -> Int? in
return Int(element)
}.filter { element in
return element != nil
}.map { element in
return element!
}
Okay, but in your example, the cast to [Int?] tells compactMap to output Int?, which means that its first map must produce Int??.
let result3 = marks.map { element -> Int?? in
return Int(element) // wrapped in extra Optional!
}.filter { element in
return element != nil
}.map { element in
return element!
}
So the first map produces double-wrapped Optionals, namely Optional(Optional(86)), Optional(Optional(45)), Optional(nil), Optional(Optional(76)).
None of those is nil, so they all pass thru the filter, and then they are all unwrapped once to give the result you're printing out.
The Swift expert who responded to my report admitted that there is something counterintuitive about this, but it's the price we pay for the automatic behavior where assigning into an Optional performs automatic wrapping. In other words, you can say
let i : Int? = 1
because 1 is wrapped in an Optional for you on the way into the assignment. Your [Int?] cast asks for the very same sort of behavior.
The workaround is to specify the transform's output type yourself, explicitly:
let result3 = marks.compactMap {element -> Int? in Int(element) }
That prevents the compiler from drawing its own conclusions about what the output type of the map function should be. Problem solved.
[You might also want to look at the WWDC 2020 video on type inference in Swift.]

Optional vs Bound value assigning var from array

I want to check if there is a value in a array and if so assign to a String using a if-left statement:
if let scoreValue = scoreValueArray[element!]{
// do something with scoreValue
}
Error: Bound value in a conditional binding must be of optional type
So tried changing the ! to ? but error persists.
Any input appreciated.
scoreValueArray is an array of strings, where a String value is appended to array if a condition is met, then array is saved to NSUserdefaults.
So element is a int which corresponds to a index in the array, bt only if the index is occupied with a String, so
scoreValueArray[element!]
could return an 'Index out of bounds', hence want to use the if-let.
Although the accepted answer clearly puts why optional binding is not available in the current implementation, it doesn't provide with a solution.
As it is shown in this answer, protocols provide an elegant way of safely checking the bounds of an array. Here's the Swift 2.0 version:
extension Array {
subscript (safe index: Int) -> Element? {
return indices ~= index ? self[index] : nil
}
}
Which you can use like this:
let fruits = ["Apple", "Banana", "Cherry"]
if let fruit = fruits[safe: 4] {
// Do something with the fruit
}
It's not clear what type your scoreValueArray is, but for the sake of this answer, I'm going to assume it's an array of Int.
var scoreValueArray: Array<Int>
Now, if we look the definition of the Array struct, we'll find this:
struct Array<T> : MutableCollectionType, Sliceable {
// other stuff...
subscript (index: Int) -> T
// more stuff
}
So, calling the subscript method on our array (which is what we do when we say scoreValueArray) returns a non-optional. And non-optionals cannot be used in the conditional binding if let/if var statements.
We can duplicate this error message in a more simple example:
let foo: Int = 3
if let bar = foo {
// same error
}
This produces the same error. If we instead do something more like the following, we can avoid the error:
let foo: Int? = 3
if let bar = foo {
// perfectly valid
}
This is different from a dictionary, whose subscript method does return an optional (T?). A dictionary will return a value if the key passed in the subscript is found or nil if there is no value for the passed key.
We must avoid array-index-out-of-bounds exceptions in the same way we always do... by checking the array's length:
if element < scoreValueArray.count {
scoreValue = scoreValueArray[element]
}

swift, optional unwrapping, reversing if condition

Let's say I have function which returns optional. nil if error and value if success:
func foo() -> Bar? { ... }
I can use following code to work with this function:
let fooResultOpt = foo()
if let fooResult = fooResultOpt {
// continue correct operations here
} else {
// handle error
}
However there are few problems with this approach for any non-trivial code:
Error handling performed in the end and it's easy to miss something. It's much better, when error handling code follows function call.
Correct operations code is indented by one level. If we have another function to call, we have to indent one more time.
With C one usually could write something like this:
Bar *fooResult = foo();
if (fooResult == null) {
// handle error and return
}
// continue correct operations here
I found two ways to achieve similar code style with Swift, but I don't like either.
let fooResultOpt = foo()
if fooResult == nil {
// handle error and return
}
// use fooResultOpt! from here
let fooResult = fooResultOpt! // or define another variable
If I'll write "!" everywhere, it just looks bad for my taste. I could introduce another variable, but that doesn't look good either. Ideally I would like to see the following:
if !let fooResult = foo() {
// handle error and return
}
// fooResult has Bar type and can be used in the top level
Did I miss something in the specification or is there some another way to write good looking Swift code?
Your assumptions are correct—there isn't a "negated if-let" syntax in Swift.
I suspect one reason for that might be grammar integrity. Throughout Swift (and commonly in other C-inspired languages), if you have a statement that can bind local symbols (i.e. name new variables and give them values) and that can have a block body (e.g. if, while, for), those bindings are scoped to said block. Letting a block statement bind symbols to its enclosing scope instead would be inconsistent.
It's still a reasonable thing to think about, though — I'd recommend filing a bug and seeing what Apple does about it.
This is what pattern matching is all about, and is the tool meant for this job:
let x: String? = "Yes"
switch x {
case .Some(let value):
println("I have a value: \(value)")
case .None:
println("I'm empty")
}
The if-let form is just a convenience for when you don't need both legs.
If what you are writing is a set of functions performing the same sequence of transformation, such as when processing a result returned by a REST call (check for response not nil, check status, check for app/server error, parse response, etc.), what I would do is create a pipeline that at each steps transforms the input data, and at the end returns either nil or a transformed result of a certain type.
I chose the >>> custom operator, that visually indicates the data flow, but of course feel free to choose your own:
infix operator >>> { associativity left }
func >>> <T, V> (params: T?, next: T -> V?) -> V? {
if let params = params {
return next(params)
}
return nil
}
The operator is a function that receives as input a value of a certain type, and a closure that transforms the value into a value of another type. If the value is not nil, the function invokes the closure, passing the value, and returns its return value. If the value is nil, then the operator returns nil.
An example is probably needed, so let's suppose I have an array of integers, and I want to perform the following operations in sequence:
sum all elements of the array
calculate the power of 2
divide by 5 and return the integer part and the remainder
sum the above 2 numbers together
These are the 4 functions:
func sumArray(array: [Int]?) -> Int? {
if let array = array {
return array.reduce(0, combine: +)
}
return nil
}
func powerOf2(num: Int?) -> Int? {
if let num = num {
return num * num
}
return nil
}
func module5(num: Int?) -> (Int, Int)? {
if let num = num {
return (num / 5, num % 5)
}
return nil
}
func sum(params: (num1: Int, num2: Int)?) -> Int? {
if let params = params {
return params.num1 + params.num2
}
return nil
}
and this is how I would use:
let res: Int? = [1, 2, 3] >>> sumArray >>> powerOf2 >>> module5 >>> sum
The result of this expression is either nil or a value of the type as defined in the last function of the pipeline, which in the above example is an Int.
If you need to do better error handling, you can define an enum like this:
enum Result<T> {
case Value(T)
case Error(MyErrorType)
}
and replace all optionals in the above functions with Result<T>, returning Result.Error() instead of nil.
I've found a way that looks better than alternatives, but it uses language features in unrecommended way.
Example using code from the question:
let fooResult: Bar! = foo();
if fooResult == nil {
// handle error and return
}
// continue correct operations here
fooResult might be used as normal variable and it's not needed to use "?" or "!" suffixes.
Apple documentation says:
Implicitly unwrapped optionals are useful when an optional’s value is confirmed to exist immediately after the optional is first defined and can definitely be assumed to exist at every point thereafter. The primary use of implicitly unwrapped optionals in Swift is during class initialization, as described in Unowned References and Implicitly Unwrapped Optional Properties.
How about the following:
func foo(i:Int) ->Int? {
switch i {
case 0: return 0
case 1: return 1
default: return nil
}
}
var error:Int {
println("Error")
return 99
}
for i in 0...2 {
var bob:Int = foo(i) ?? error
println("\(i) produces \(bob)")
}
Results in the following output:
0 produces 0
1 produces 1
Error
2 produces 99