Oscar Apeland
Oscar Apeland

Reputation: 6662

Map only non-nil values

I am serialising some json into objects with a failable json initialiser like this:

 sections = {
        let sectionJsons = json["sections"] as! [[String:AnyObject]]
        return sectionJsons.map {
            DynamicSection($0)
        }
    }()

DynamicSection's init:

init?(_ json:[String:AnyObject]) {
    super.init()
    //Boring stuff that can fail

I want to only append the DynamicSections that passed the init to sections. How can I accomplish this?

I can use filter+map like

return sectionJsons.filter { DynamicSection($0) != nil }.map { DynamicSection($0)! }

But that leads to initing the DynamicSection twice, which i'd like to avoid. Is there any better way to do this?

Upvotes: 29

Views: 19256

Answers (2)

dfrib
dfrib

Reputation: 73186

You can use compactMap:

return sectionJsons.compactMap { DynamicSection($0) }

Example:

struct Foo {
    let num: Int
    init?(_ num: Int) {
        guard num % 2 == 0 else { return nil }
        self.num = num
    }
}

let arr = Array(1...5) // odd numbers will fail 'Foo' initialization
print(arr.compactMap { Foo($0) }) // [Foo(num: 2), Foo(num: 4)]

// or, point to 'Foo.init' instead of using an anonymous closure
print(arr.compactMap(Foo.init))   // [Foo(num: 2), Foo(num: 4)]

Whenever you see a chained filter and map, compactMap can generally be used as a good alternative approach (not just when using the filter to check nil entries).

E.g.

// non-init-failable Foo
struct Foo {
    let num: Int
    init(_ num: Int) {
        self.num = num
    }
}

let arr = Array(1...5) // we only want to use the even numbers to initialize Foo's

// chained filter and map
print(arr.filter { $0 % 2 == 0}.map { Foo($0) })   // [Foo(num: 2), Foo(num: 4)]

// or, with compactMap
print(arr.compactMap { $0 % 2 == 0 ? Foo($0) : nil }) // [Foo(num: 2), Foo(num: 4)]

Upvotes: 40

pierre23
pierre23

Reputation: 3936

For Swift 3.0 and above:

return sectionJsons.compactMap { DynamicSection($0) }

Upvotes: 30

Related Questions