Proper way to concatenate optional swift strings?

Somewhere, I believe in the swift book, I ran into this pattern, from when before you could have multiple lets in a single if:

class User {
    var lastName : String?
    var firstName : String?

    var fullName : String {
        switch (firstName, lastName) {
        case (.Some, .Some):
            return firstName! + " " + lastName!

        case (.None, .Some):
            return lastName!

        case (.Some, .None):
            return firstName!

        default:
            return ""
        }
    }

    init(lastName:String?, firstName:String?) {
        self.lastName = lastName
        self.firstName = firstName
    }
}

User(lastName: nil, firstName: "first").fullName        // -> "first"
User(lastName: "last", firstName: nil).fullName         // -> "last"
User(lastName: nil, firstName: nil).fullName            // -> ""
User(lastName: "last", firstName: "first").fullName     // -> "first last"

An even briefer solution, given swift 3.0:

var fullName : String {
    return [ firstName, lastName ].flatMap({$0}).joined(separator:" ")
}

Here is an alternative method:

let name = 
(person.first != nil && person.last != nil) ? 
person.first! + " " + person.last! : 
person.first ?? person.last!

Sometimes simple is best:

let first = p.first ?? ""
let last = p.last ?? ""
let both = !first.isEmpty && !last.isEmpty
let full = first + (both ? " " : "") + last

This works if there is no first or last, if there is a first but no last, if there is a last but no first, and if there are both a first and a last. I can't think of any other cases.

Here's an idiomatic incorporation of that idea into a calculated variable; as an extra benefit, I've allowed full to be nil just in case both the other names are nil:

struct Person {
    var first : String?
    var last : String?
    var full : String? {
        if first == nil && last == nil { return nil }
        let fi = p.first ?? ""
        let la = p.last ?? ""
        let both = !fi.isEmpty && !la.isEmpty
        return fi + (both ? " " : "") + la
    }
}

compactMap would work well here, combined with .joined(separator:):

let f: String? = "jo"
let l: String? = "smith"

[f,l] // "jo smith"
  .compactMap { $0 }
  .joined(separator: " ")

It doesn't put the space between if one is nil:

let n: String? = nil

[f,n] // "jo"
  .compactMap { $0 }
  .joined(separator: " ")

Tags:

Swift