17

In Swift arrays you can do:

var myArray = [1,2,3,4]

myArray.forEach() { print($0 * 2) }

myArray.map() { print($0 * 2) }

They both do the same thing. The only difference is .map also returns an array of voids as well, [(),(),(),()], which gets unused. Does that mean .map performs worse than .forEach when it's not assigning to anything?

5
  • 3
    Generating an array ultimately means it "performs worse" for some irrelevant value. However, the actual problem is one approach is less-ideal code as it is not precise about the intent since the result of map are discarded. Conversely, there is a strong argument for ensuring that all map operations are only for the result - and not used for any [write] side-effects. With that in mind, there is only one 'semantically correct' option regardless of any 'performance'. These simple Not Tricky Coding Choices (TM) are key to reducing frustration/wtf for those who work on the code after you. Commented Aug 2, 2017 at 1:28
  • If people wanted to reduce "wtf" they should not have named a thing that goes through every item in an array and runs a function on it, "map". This is the kind of thing terminology that British math professors thought up in the 1800s. We can do better! It should be ".applyToEach"! Sigh. Commented Aug 4, 2017 at 8:45
  • As well, if you assign .map() to _, then it doesn't generate a return. (Compiler optimization! Yay) Commented Aug 4, 2017 at 9:31
  • Can you please point to the documentation where it says it wont generate any return? Looking at the source code of map, they are generating an array for a call. Then how does compiler optimizes? My understanding of _ is that it just ignores the returned value. Commented Aug 4, 2017 at 22:23
  • @adev You're right, it will generate a void return if you don't return anything from inside the closure. I was wrong. As for the "for in" loop, in my testing it was the fastest. That being said, it doesn't chain as nicely after filter as map does, but I guess you could always for elem in myArray.filter() { $0 != 42 } {... Commented Aug 5, 2017 at 0:29

3 Answers 3

25

In Swift as per Apple's definition,

map is used for returning an array containing the results of mapping the given closure over the sequence’s elements

whereas,  

forEach calls the given closure on each element in the sequence in the same order as a for-in loop.

Both got two different purposes in Swift. Even though in your example map works fine, it doesn't mean that you should be using map in this case.

map eg:-

let values = [1.0, 2.0, 3.0, 4.0]
let squares = values.map {$0 * $0}

[1.0, 4.0, 9.0, 16.0] //squares has this array now, use it somewhere

forEach eg:-

let values = [1.0, 2.0, 3.0, 4.0]
values.forEach() { print($0 * 2) }

prints below numbers. There are no arrays returned this time.

2.0

4.0

6.0

8.0

In short to answer your questions, yes the array generated from map is wasted and hence forEach is what you should use in this case.

Update:

OP has commented that when he tested, the performance was better for map compared to forEach. Here is what I tried in a playground and found. For me forEach performed better than map as shown in image. forEach took 51.31 seconds where as map took 51.59 seconds which is 0.28 seconds difference. I don't claim that forEach is better based on this, but both has similar performance attributes and which one to use, depends on the particular use case.

forEach vs Map performance test

Sign up to request clarification or add additional context in comments.

5 Comments

Actually I just did some testing, and map is faster by about 10%. If you assign its result to _ then it does not generate a result and performs better still.
forEach and map should have similar performance in that case since functionality of forEach and map in that case is just to iterate through the list and execute closure over each element. If you look at the implementation of both in swift source code, you can see that both uses same for-in loop to iterate over the list. Map also creates a ContigousArray and stores output to it where as forEach just iterates. Unless the compiler does some magic, I am not sure why forEach would perform bad. Probably you should use for-in in that case since internally that is what used anyways.
I just tried a performance test and found that forEach is faster compared to map. Clearly, there is no definite answer for which one is faster then since it could change easily again for another usecase.
Interesting. In your case were you assigning the result of map to _ ? I started having weird bugs and errors when I assigned it to itself. Not sure what was going on.
It's all about whether you need result in response or not. If you need to save the result go with map else go for forEach
3

According to Apple Doc

.map

The map(_:) method calls the closure expression once for each item in the array. You do not need to specify the type of the closure’s input parameter, number, because the type can be inferred from the values in the array to be mapped.

.forEach(_:) Apple Doc

Calls the given closure on each element in the sequence in the same order as a for-in loop.

var myArray = [1,2,3,4]
var sampleArray = [1,2,3,4]
//myArray = myArray.forEach() { ($0 * 2) }//Not allowed
sampleArray = sampleArray.map() { ($0 * 2) }
print("sampleArray array is \(sampleArray)")//sampleArray array is [2, 4, 6, 8]

Comments

-4

.map is faster than .forEach.

Just by a hair. Note that assigning .map to _ as pictured causes the closure to be required not to return a value.

A wild compiler optimization appears!

enter image description here

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.