简体   繁体   中英

Shorter Alternative to ternary to generate empty string if nil?

I have a parameter of Type Double? . When this parameter is nil , I want to have an empty string.

I can use if (variable == nil) ? "" : String(variable!) if (variable == nil) ? "" : String(variable!) but is there a shorter alternative?

Using Optional.map and the nil-coalescing operator ?? you can do

var variable: Double? = 1.0
let string = variable.map { String($0) } ?? ""

The closure is called (and the string returned) if the variable is not nil , otherwise map returns nil and the expression evaluates to the empty string.

I don't see a simple way to simplify your code. An idea is to create a Double extension like this:

extension Optional where Wrapped == Double {
    var asString: String {
        self == nil ? "" : String(self!)
    }
}

And then instead of that if condition you just use:

variable.asString

If you want to use the resulting string in another string, like this:

let string = "The value is: \\(variable)"

and possibly specify what to print when variable is nil :

let string = "The value is: \\(variable, nil: "value is nil")"

you can write a handy generic extension for String.StringInterpolation which takes any type of value and prints this and if it's an optional and also nil it prints the specified "default" string:

extension String.StringInterpolation {
    mutating func appendInterpolation<T>(_ value: T?, `nil` defaultValue: @autoclosure () -> String) {
        if let value = value {
            appendLiteral("\(value)")
        } else {
            appendLiteral(defaultValue())
        }
    }
}

Example:

var d: Double? = nil
print("Double: \(d, nil: "value is nil")")

d = 1
print("Double: \(d, nil: "value is nil")")

let i = 1
print("Integer: \(i, nil: "value is nil")")

Output on the console:

Double: value is nil
Double: 1.0
Integer: 1

Just for fun a generic approach to cover all types that conforms to LosslessStringConvertible:

extension LosslessStringConvertible {
    var string: String { .init(self) }
}

extension Optional where Wrapped: LosslessStringConvertible {
    var string: String { self?.string ?? "" }
}

var double = Double("2.7")

print(double.string)    // "2.7\n"

Property wrappers should help you give the desired result - property wrappers have a special variables wrappedValue and projectedValue that can add a layer of separation and allow you to wrap your custom logic.

wrappedValue - manipulate this variable with getters and setters. It has very less use in our case as it is of Double? type

projectedValue - this is going to be our focus as we can use this variable to project the Double as a String in our case.

The implementation is as below

@propertyWrapper
struct DoubleToString {
    private var number: Double = 0.0
    var projectedValue: String = ""
    var wrappedValue: Double?{
        get {
            return number // Not really required
        }
        set {
            if let value = newValue { // Check for nil
                projectedValue = value.description // Convert to string
                number = value
            } 
        }
    }
}

Now we create a struct which uses this wrapper.

struct NumbersTest {
    @DoubleToString var number1: Double?
    @DoubleToString var number2: Double?
}

On running the below code, we get the desired result. $number1 gives us the projectedValue and if we ignore the $ symbol we get the wrappedvalue

var numbersTest = NumbersTest()
numbersTest.number1 = 25.0
numbersTest.number2 = nil

print(numbersTest.$number1) //"25.0"
print(numbersTest.$number2) //""

By using property wrappers you can keep the variable interoperable to get both Double and String values easily.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM