1

This is my code:

class Birthgiver {}
class Son: Birthgiver {}
class Daughter: Birthgiver {}

class BirthgiverHolder {
    let sons: [Son]
    let daughters: [Daughter]

    init(birthGivers: [Birthgiver]) {
        // How to initializer both sons and daugthers in 1 loop?
        // This is my current way (looping twice):
        sons = birthGivers.compactMap { $0 as? Son }
        daughters = birthGivers.compactMap { $0 as? Daughter }
    }
}

I am looping twice over the array birthGivers. Is there some way I can initialize both sons and daughters while only looping once over birthGivers? I don't want to mark the arrays as vars.

2
  • Unless birthGivers has 100s of objects, I wouldn't worry about it. The trivial overhead of looping twice outweighs the more difficult to read code you would need to write to loop once. Commented Apr 15, 2019 at 18:50
  • @rmaddy In the real code there are lots of subclasses of Birthgiver, so in my case it could benefit a lot (without profiling it, but I am still wondering if there is an answer) Commented Apr 15, 2019 at 18:53

3 Answers 3

3

Option 1: Have local vars and populate your constants when you’re done:

init(birthGivers: [Birthgiver]) {
    var sons: [Son] = []
    var daughters: [Daughter] = []

    for child in birthGivers {
        switch child {
            case let son as Son: sons.append(son)
            case let daughter as Daughter: daughters.append(daughter)
            default: break
        }
    }

    self.sons = sons
    self.daughters = daughters
}

Option 2: You can achieve it with reduce(into:), too (though I personally find the above more readable):

init(birthGivers: [Birthgiver]) {
    (sons, daughters) = birthGivers.reduce(into: ([], [])) {
        switch $1 {
        case let son as Son: $0.0.append(son)
        case let daughter as Daughter: $0.1.append(daughter)
        default: break
        }
    }
}

Option 3: Stick with compactMap approach:

init(birthGivers: [Birthgiver]) {
    sons = birthGivers.compactMap { $0 as? Son }
    daughters = birthGivers.compactMap { $0 as? Daughter }
}

That last option is perfectly adequate in most cases. You’d need an extraordinary number of records for the performance difference to be observable.

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

2 Comments

([], []) would be sufficient as the initial result in version #2, the type can be inferred automatically.
@MartinR - Thanks.
0

You can refactor that code to loop once but it's far more complicated and harder to read. Unless you have hundreds of objects in the birthgiver array, this is probably not worth the effort.

class BirthgiverHolder {
    let sons: [Son]
    let daughters: [Daughter]

    init(birthGivers: [Birthgiver]) {
        var sons = [Son]()
        var daughters = [Daughter]()
        for giver in birthGivers {
            if let son = giver as? Son { sons.append(son) }
            else if let daughter = giver as? Daughter { daughters.append(daughter) }
        }

        self.sons = sons
        self.daughters = daughters
    }
}

Comments

0

As mentioned, this isn't likely to make any performance difference. If you really want to try it though, you could do it with reduce:

(sons, daughters) = birthGivers.reduce(into: ([Son](),[Daughter]())) {arrays, element in
    if let element = element as? Son {
        arrays.0.append(element)
    } else if let element = element as? Daughter {
        arrays.1.append(element)
    }
}

Here we create a tuple of two arrays, assign the elements to them as appropriate, and then assign them to your class properties.

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.