Bigair
Bigair

Reputation: 1592

Rewite for in 0...n loop by map

I have a swift code like below

var items: [Item] = []
for i in 0...20 {
    items.append( makeItem(atIndex: i) )
}

would it be possible to rewrite above like

var items: [Item] = [0...20].map {
   makeItem(atIndex: i)
}

Upvotes: 0

Views: 456

Answers (3)

Gustavo Vollbrecht
Gustavo Vollbrecht

Reputation: 3256

To get your exact syntax, you can create the extension:

extension Array where Element == ClosedRange<Int> {

    @discardableResult
    func map(_ f: @escaping(Int) -> Void) -> [Int]{

        var result : [Int] = []

        for i in 0 ..< self[0].count {
            result.append(i)
            f(i)
        }

        return result
    }
}

and call it like this:

let items = [0...50].map {
    print($0) // current index
}

print(items)

Note that items will be [Int], this solves your question before your edits, when you just wanted to write a for loop like this:

[0...50].map {
    print($0) // current index
}

Upvotes: 0

Nicolas Miari
Nicolas Miari

Reputation: 16256

The syntax [0...20] Is an array containing a single closed range of Int, not an array of Int (which is what you would typically want to call map() on ), so what you really want is:

let integers = [
    0, 1, 2, 3, 4, 5, 
    6, 7, 8, 9, 10, 11, 
    12, 13, 14, 15, 16, 
    17 ,18, 19, 20]

integers.map{
   // your code here
}

(My original answer missed this distinction, I have corrected it now. See other answers on how to convert a range into an array of Int)


Both will execute the block within the brackets 21 times, one for each integer in the array.

But in contrast to the for loop, the map() function is expected to return an array of some type, where each element is obtained by transforming each of the elements in the original array (in this case, the integers 0 through 20) into an instance of the resulting type. For example:

let textRepresentations: [String] = integers.map {
    return "\($0)"
}

// Now textRepresentations contains "0", "1", etc.

If your goal is to assemble an array out of objects created based on those 21 integers, map() gives you a more elegant solution than you would have done in a more "C-like" way, e.g.:

var textRepresentations: [String] = []
for i in 0 ... 20 {
     textRepresentations.append("\(i)")
}

But if you only need to execute some logic once for each of those integers, with no array left as a result, using map() (and discarding the result) will look awkward and confusing to people have to maintain that code (including your future self).

Additionally, a more "Swifty" alternative to the for ... in loop is foreach (which looks a bit more like map(), minus the returned array):

integers.foreach {
    // (use $0 instead of i)
}

...or, more verbosely:

integers.foreach { (element) in
    // (use `element`, of course)
}

Upvotes: 1

EmilioPelaez
EmilioPelaez

Reputation: 19912

It's possible, just don't wrap the range in an array, so use (0...20) instead of [0...20]

You can even pass the name of the function and you won't have to create a closure with a single call.

let items = (0...20).map(makeItem)

Upvotes: 3

Related Questions