Programming in Swift · Closures and Collections | Ray Wenderlich

It will always be specified or inferred. The output type of the map methods is determined by the type returned by the closure, not the type of the input sequence.

It should. Here is an example, with an array of Int as input. The closure produces an array of Doubles. With map, you end up with an array of arrays of Doubles. With flatMap, and the same closure, you get a single array of Doubles.

let intArray = [1, 2, 3]
let mapArray = intArray.map {item in [Double(item), Double(item) / 2]}
print(mapArray)

[[1.0, 0.5], [2.0, 1.0], [3.0, 1.5]]

let flatMapArray = intArray.flatMap {item in [Double(item), Double(item) / 2]}
print(flatMapArray)

[1.0, 0.5, 2.0, 1.0, 3.0, 1.5]

1 Like

It’s hit and miss for me, but I think it may have more to do with me not being very skilled with writing code yet. It’s usually when the input is an array of arrays…

For example, this works:

45%20PM

But not this:

And neither does this:

I’m not too sure how to make it work… Adding square braces didn’t help (error saying there’s no subscript available).

numbers is an array of two elements, each of which is an array of Int. So it will be passing an array of Int into the closure. As the compiler error indicates, you can’t use Double() on an array of Int.

Same thing. numberStrings is an array of two elements, each of which is an array of String, so it will be passing an array of String to the closure. You can’t use Int() on an array of String.

In both cases, once you grasp that the parameter passed into the closure is an array, you could put another map call inside the closure. That inner map would now get individual Ints or Strings, so its closure could use Double() or Int().

The first one works because it is passing each of the two arrays of Int into the closure, and the closure just returns the passed in array. flatMap gets a total of two arrays returned, and joins them into one. For the other two, the closure to flatMap should also be returning an array, so flatMap can join them. As Picard would say, your task is to make it so.

2 Likes

Hey Catie, thanks for your help. Is there an official answer for the extra credit challenge you mention at the very end of the video? It asks us to use the flatMap function with longer syntax. I think im almost there, but my compiler is not yet compiling. Let me know if you have an answer the question in the video I can use to reference! thanks

Hi! I actually didn’t write an official solution for that bit of extra credit.

If you’d like to post what you’ve got already, I’d be happy to take a look. :]

sure, here is my shorthand way of writing the second closure:

32%20PM

Here are two tidbits for ya.

  1. A shortened version of dwarvesAfterM:
let dwarvesAfterM = arrayOfDwarfArrays.flatMap {
  $0.filter { $0 > "M" }
}.sorted()
  1. And a possible solution for the bonus challenge, using dwarvesAfterM as an example problem:
// `flatMap` as a `for` loop - I've leave this one as a bonus challenge!
var dwarvesAfterM2: [String] = []
for dwarfArray in arrayOfDwarfArrays {
  dwarvesAfterM2 += dwarfArray.filter { $0 > "M" }
  dwarvesAfterM2.sort()
}

Hi Catie.

I’m a long time C/C++ & Obj-C programmer, and I’ll preface this with the fact that I utterly loathe Swift. I can’t believe this was ‘designed’.

One of the issues I’m struggling with is Closures and how ludicrous it is that you can just keep removing syntax and reducing something vaguely readable into something that isn’t - and Apple say this is easy and friendly and readable! Ha!!! I also can’t abide K&R so I always have to refactor into Allman to more clearly show scope.

In your Closures lesson you demonstrate this, but I am sticking to the full syntax as it’s marginally more readable, but it’s lead me to have a question:

I’ve written this code, and it works in the simulator BUT despite it working I still get the red exclamation point on the line:

let ns = getNetString()     <- ! Use of unresolved identifier 'getNewString'

Here’s the full code running in a Playground:

let multiply: (Int, Int) -> Int =
{
    (a: Int, b: Int) -> Int in 
    
    let s = "Arrghh!!! Swift!!!"
    let ns = getNewString()      
    
    if a < b
    {
        print(a, b, s, ns)
    }
    else
    {
        print(b, a, s, ns)
    }

    print ("Result of \(a) * \(b) = \(a * b)")
    
    return a * b
}

func getNewString() -> String
{
    return "We hates it we does"
}

multiply(4, 2)

Result in console window:

**2 4 Arrghh!!! Swift!!! We hates it we does**

**Result of 4 * 2 = 8**

So, why is it working when the editor seems to think it shouldn’t? And is this kind of thing allowed? i.e. calling other methods within an object from inside a closure. Or is this only working because the called method is returning a fixed string i.e. constant and therefore not run-time dependant?

Why did they design it so there are so many ways to write it? It’s just making it harder to read other peoples code and quickly read and understand the code itself?!

Hi Anne Marie,

That’s an interesting playground bug! Playgrounds are meant to run from top to bottom, so to get rid of the error just move getNewString above multiply. I’m really not sure why/how it’s managing to run anyway :[

Dealing with the 5,000 ways to write a closure can definitely be frustrating! It got easier to read the shorthand syntax(es) with time and practice, but I often prefer to be more explicit.

1 Like

Hi!

First, you’re definitely not alone with your opinion on bracing: Swift bracing — Erica Sadun. However, the Swift community settled on 1TBS pretty early on, so you’ll want to use your own linting rules if you’ll be converting others’ work for your own projects.

Have you held the command key while mousing over braces or parentheses yet in Xcode? That highlights the matching symbol. You may find it helpful for visualizing scope; I certainly do when there’s enough nesting going on.

We don’t get into generics in this course, but Catie and I use something like Allman style when generic functions get complex enough, and the opening brace no longer seems to have a most-meaningful connection to what precedes it. I’d be interested to know how you’d format this (an example we use in our own style guide):

/*:
### Multipline `where` clauses
When there is more than one `where` clause, each requires its own line, and is indented below the `where` keyword. The following opening `{` will appear at the beginning of the line after the last requirement.
*/

protocol PAT {
  associatedtype Associatedtype
}

func function<
  Parameter0: PAT,
  Parameter1: PAT
>(
  parameter0: Parameter0,
  parameter1: Parameter1
)
where
  Parameter0.Associatedtype: Protocol,
  Parameter1.Associatedtype: Protocol
{
  // 🦁
  // 🐯
  // 🐻
}

As for shorthand syntax in closures, I think your example is a great illustration for a case where positional shorthand names are preferable. Within the closure, you’re quickly giving meaning to the parameters, so they can be given meaningful names. But considering all the closure knows about its parameters, to start, is that they’re integers, and their positions, $0 and $1 are clearer to me than a and b, or any alternative. I wish we had the option to use positional syntax in functions too, for cases like this!

It will be interesting to see if you come to a similar opinion after getting over the initial pain of learning Swift closure syntax – I’d love to hear back from you about it (in maybe a year’s time or so?) after you’ve had ample time to experiment and come to find your own standards.

let multiply: (Int, Int) -> Int = {
  let (min, max) =
    $0 < $1
    ? ($0, $1)
    : ($1, $0)
  print(
    min, max,
    "Arrghh!!! Swift!!!",
    getNewString()
  )

  let product = $0 * $1
  print("Result of \($0) * \($1) = \(product)")
  return product
}

Hi Catie & Jessie

Thanks for taking the time to read and reply with such useful info to my previous questions.

It really seems like I’m never going to get anywhere with swift, especially because of closure syntax and all this obfuscated / truncated code.

I’ve been trying to take what you said above and apply it to the next big chapter / video: Closures and Collections, and I admit, I’m so utterly lost and frustrated that I feel like giving up.

I’m writing because I’d like to ask for help in understanding what’s going on with the following syntax. I understand that there is less readability because of type inference going on, but I can’t read this, and I certainly could never come up with this on my own.

1.    let dwarvesAfterM = arrayOfDwarfArrays.flatMap 
2.    { 
3.        array -> [String] in
4.            return array.filter
5.            { 
6.               dwarf -> Bool in
7.                   dwarf > "M"
8.            }
9.    }

So, on line 3, you’re saying the ‘flatmap’ method takes an array and returns a string right?

So line 4, you’re using another closure to find and return that string… however…

In line 6, you’re saying the input is a ‘dwarf’ object (no type?! so what’s it inferring? and where is this being defined?!) and it’s returning a BOOL!??!?

I thought it was supposed to return a string?

I see on line 7, if we can ASSUME that ‘dwarf’ objects are strings, then you’re ‘filtering’ to return a string IF AND ONLY IF it meets this condition, but where does this returned BOOL go and why is it ignored and why doesn’t it become the return type of the thing returning back into the flatmap closure?

I’m so lost.

I apologise if this is trivial to others, but I understand procedural languages. This ‘functional’ stuff feels like nonsense to me. And utterly overcomplicating things. I miss Obj-C :frowning:

EDIT:

On a similar note, how do you even begin to work out how to turn the code signature from it’s Xcode prototype into the code from your example?! They don’t even resemble each other:

[]

Hi again!

I think you have a couple small misunderstandings here that are hopefully easy to clear up. Mostly, I think you’re confusing the inputs/outputs of flatMap/filter with those of their closure arguments.

I’m going to go through your questions, and at the end I’ll include an example of a for loop that gets you the same result as the flatMap/filter combo.

So, on line 3, you’re saying the ‘flatmap’ method takes an array and returns a string right?

Important differences:
1 - flatMap acts on a multidimensional array! In this case it’s an array of string arrays. The closure takes a one-dimensional string array.
2 - Both flatMap and the closure return a string array. The closure returns whatever remains of the string array that was passed into it. flatMap returns the contents of all the arrays returned by the closure, flattened into a one-dimensional array.

In line 6, you’re saying the input is a ‘dwarf’ object (no type?! so what’s it inferring? and where is this being defined?!) and it’s returning a BOOL!??!?

I thought it was supposed to return a string?

Usually, I recommend option-clicking on things to find out what type they are, but that’s not working for me in the closure parameter lists right now :[

For filter, the input for the closure is an element from the array filter is called by. In this case, that input is a string that I’ve named dwarf. The closure returns a bool. filter is returning a string array, and the bool determines whether each string (or dwarf, in this case) is included in that returned array.

And now, the for loop I promised ( I tried to use your bracing style here :] )

var dwarvesAfterM: [String] = []
// flatMap
for dwarfArray in arrayOfDwarfArrays
{
  // filter
  var dwarves: [String] = []
  for dwarf in dwarfArray 
  {
    if dwarf > "M" 
    {
      dwarves.append(dwarf)
    }
  }

  dwarvesAfterM += dwarves
}

Last note: I agree that the placeholders Xcode gives you don’t feel super helpful in these cases, and I rarely use them. I’ve often option-clicked on reduce/filter/etc. to look at the longer descriptions of the parameters when I’ve gotten stuck.

2 Likes

Hi
if by this point in this course I’m finding it hard to grasp, and I find that I’m not remembering everything, am I in trouble, or should I just carry on ?

Hi Hammam!

Fortunately, it’s not necessary to have it all memorized in order to accomplish wonderful things. If you’re either enjoying what you’re learning, or you know it will be helpful to you, I recommend persevering. You should expect the process to be challenging – it certainly was for me! :smiley_cat:

Someone else had a similar question recently. My answer to them is my answer to you, with the addition that you don’t even need to understand everything, let alone have it easily retrievable in your memory, in order to move along. A benefit of the recorded material is that you can come back to it whenever you’d like.

1 Like

Hi hammam,

I’m doing this course for the first time too. And this video was definitely the one the took me the longest so far. AND I come from a programming background! I had to do it a few times to grasp closures, so don’t worry, you’re not alone.
My understanding is that closures are basically functions (without a name or the ‘func’ keyword) that you can pass around like variables. They can be called at a later time. They remember the scope of the variables they use.

I also had the issue where I wasn’t remembering everything, I realised this in the Collections videos (Arrays, Dictionaries, Sets…) and this is what I did: I started the whole course again, and instead of just watching Jessie and Catie explain the concepts, I typed out everything that they were typing out. I didn’t download the starter playgounds. Now I have a playground in XCode with separate pages for each concept for each video. I’ve added my own helpful comments as i went along and it’s something I can refer back to without having to watch the videos again. I found that typing everything out as Jessie and Catie did it helped me to cement a lot of things - a lot more than simply doing the challenge or just downloading the end playground.

Also if you get stuck, then ask on here or ask someone. There are no dumb questions, there is always someone out there thinking the exact same thing you are. There are so many times I had to turn to my colleague when watching this one and ask things like “how did they get away with not defining this thing?”

All the best!

1 Like

Hi
thank you both for the encouraging words, I was beginning to think it was just me, thank you I shall soldier on hah

1 Like

Hi guys I have got this far OK I think, but this Closures and Collections
has knocked me back. Its like I’ve had a brain freeze, at the end with
the mini challenge shortening the syntax & writing it as a ‘for’ loop I am just
sitting here blank…
Its like writers block does this happen often or is it just me closures & collections
just wont sink in??

Is there any extra material that will help me I cant watch this section a tenth time.

1 Like

This has been the hardest video so far.

Hi! I know Jessy has already pointed you to the updated version of this course. We decided to split up our Swift content this year, so the Fundamentals course doesn’t cover closures at all.

Closures and higher-order functions can be a sticking point for a lot of people! I’m currently working on a second Swift course, which will spend a lot more time on an introduction to closures and how to use methods like map.

Since that isn’t ready for you, yet, we do have a written tutorial on Functional Programming in Swift that talks about map, filter, and reduce.