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 trialReed Carson
8,306 PointsPlease 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
28,883 PointsYou'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
Martin Wilter
iOS Development Techdegree Student 8,782 PointsHi 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
8,306 PointsI 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
28,883 PointsThe 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
28,883 PointsYou'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
8,306 PointsI 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
28,883 PointsYou'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].