H K
H K

Reputation: 1275

Swift Functional Programming - is there a better way to translate a nested for loop than two map calls

i've transformed a nested for loop into a nested map call. i was wondering if there was a more elegant way to implement it.

here is a function that takes in an Array of Items and an Array of functions (Item -> Item) and returns an array with all of the functions applied to each item:

typealias Item = Dictionary<String, String>    

func updatedItems(items: Array<Item>, fns: Array<Item -> Item>) -> Array<Item> {
    return items.map {
        item in

        var itemCopy = item
        fns.map { fn in itemCopy = fn(itemCopy) }

        return itemCopy
    }
}

Upvotes: 2

Views: 1191

Answers (2)

Martin R
Martin R

Reputation: 539955

The inner map() call does not return a new array, it is done merely for the side-effects. This works, but is sometimes "frowned-upon" (see for example Higher order function: "Cannot invoke 'map' with an argument list of type '((_) -> _)'").

The "proper" way to apply all functions in turn to a single item is to use reduce:

func updatedItems(items: Array<Item>, fns: Array<Item -> Item>) -> Array<Item> {
    return items.map {
        item in
        fns.reduce(item) {
            (curItem, fn) in
            fn(curItem)
        }
    }
}

or with shorthand argument names:

func updatedItems(items: Array<Item>, fns: Array<Item -> Item>) -> Array<Item> {
    return items.map {
        fns.reduce($0) { $1($0) }
    }
}

Note also that you can shorten the array notation to

func updatedItems(items: [Item], fns: [Item -> Item]) -> [Item] { ... }

Upvotes: 4

Mundi
Mundi

Reputation: 80273

Another solution is to use enumerate in order to also have access to the index.

return items.enumerate().map { (idx, item) in
   let fn = fns[idx] as <Item -> Item>
   return fn(item)
}

Upvotes: 2

Related Questions