Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

iOS Closures in Swift 2 Building Standard Library Functions Flat Map

Reed Carson
Reed Carson
8,306 Points

Please explain what this closure is doing

struct Post {
    var content: String
    var tags: [String]
}

let blog = [
    Post(content: "Hello, world!", tags: ["first", "programming"]),
    Post(content: "Just another short post", tags: ["general"])
]

blog.map {$0.content}

closures are already a bit mystifying, and map often seems magical. Ive gotten a better grasp but in this example I really can't see what $0.content is doing. That's not a function as far as I can tell. Can someone please explain whats going on here?

4 Answers

Anjali Pasupathy
Anjali Pasupathy
28,883 Points

You're right, I apologize. I mixed up that particular functionality between map and flatMap. map would return and array of type [String?] with the nil values in place; flatMap is the function that would weed out the nil values and return an array of type [String].

The declaration for map is this: func map<T>(@noescape transform: (Element) throws -> T) rethrows -> [T]

I'm not entirely sure of what @noescape does, but as you can see, the closure takes an Element from the array and returns a value of some generic type T. The closure from the example is the (very short) shorthand of the following code:

blog.map(transform: {
        blogPost in
        return blogPost.content
    }
)

map defines what parameter we're taking in and what the return type is, and we only really need one line of code to access what we're returning. Because of this, the line "blogPost in" and the keyword "return" are unnecessary, which is why we can use such a short shorthand.

blog.map {
    // blogPost in
        // We don't need that line, since we can access the blogPost parameter using $0
    /* return */ $0.content
        // We don't need the return keyword because we only have one line of code
        // The compiler assumes that this one line of code is the object we're returning
}

The map function then takes what's being returned from the transform closure (in this case, the content of the blogPost), and appends it to an array with elements of the same type as the return type.

Hopefully that explanation makes sense! (x

Hi Reed,

I think if you put it in the context of the map function we recreated it might be a bit clearer:

// Recreate map as mapRecreated
extension Array {
    func mapRecreated<T>(transform: Element -> T) -> [T] {
        var result = [T]()

        for x in self {
            result.append(transform(x))
        }

        return result
    }
}

struct Post {
    var content: String
    var tags: [String]
}

let blog = [
    Post(content: "Hello, world!", tags: ["first", "programming"]),
    Post(content: "Just another short post", tags: ["general"])
]

blog.mapRecreated { $0.content }
Reed Carson
Reed Carson
8,306 Points

I think i understand now. What was confusing me was the seemingly magical leaps of inference the compiler made with the property we passed it. Just saying "$0.content" seemed like way too little information for the compiler so say "hey thats a function". I see it now, and I suppose I'll get used to it but dang that seems a little too shorthand. Maybe what's confusing is that it also seems almost out of place in Swift, because everything else is so clear.

Anjali Pasupathy
Anjali Pasupathy
28,883 Points

The map function is going through the array of Post objects, extracting the content property of each Post object, and putting each content property into an array. If you were to set some variable or constant equal to blog.map { $0.content }, you would get the following result:

let contentsArray = blog.map { $0.content }
/*
 contentsArray = [
     "Hello, world!",
     "Just another short post"
 ]
*/

It is worth mentioning that if the content property were an optional String, the map function would unwrap the optional, and if the content isn't nil, it would append the content to an array of type [String] (NOT [String?]):

struct Post {
    var content: String?
    var tags: [String]
}

let blog = [
    Post(content: "First post!", tags: ["first","this is a post"]),
    Post(content: nil, tags: []),
    Post(content: "Hello, world!", tags: ["hello","programming"]),
    Post(content: nil, tags: ["why am i tagging an empty post", "oh well", "nothing", "empty"])
]

let contentsArray = blog.map { $0.content }
/*
 contentsArray: [String] = [
     "First post!",
     "Hello, world!"
 ]
*/

I hope this helps!

Anjali Pasupathy
Anjali Pasupathy
28,883 Points

You're right, I apologize. I mixed up that particular functionality between map and flatMap. map would return and array of type [String?] with the nil values in place; flatMap is the function that would weed out the nil values and return an array of type [String].

Reed Carson
Reed Carson
8,306 Points

I thought flatMap was the one that weeded out optionals, and map could indeed return optionals.

Thank you, I understand what it does, but I don't understand how it does it, because I thought map took and function and then applied that function to each element in the array, but in this case I dont see what function is being applied.

Anjali Pasupathy
Anjali Pasupathy
28,883 Points

You're right, I apologize. I mixed up that particular functionality between map and flatMap. map would return and array of type [String?] with the nil values in place; flatMap is the function that would weed out the nil values and return an array of type [String].