Search code examples
iosswiftstringfloating-pointdouble

How to format decimal in Swift to percentage


I have a function which finds percentage difference of adjacent values in an array.

var values =  [1.623,1.614,1.591,1.577,1.600,1.579,1.622]
values.reverse()
let percentages = [0] + zip(values, values.dropFirst()).map { (old, new) in
    return (100.0 * (new - old) / old)
}
print(percentages)

[0.0, -2.651048088779294, 1.3299556681444034, -1.4375000000000082, 0.8877615726062151, 1.445631678189826, 0.5576208178438598]

Is there a way i can format my values to look like the below

["+0.006%","+0.014%","+0.009%","-0.014%","+0.013%","-0.027%"]


Solution

  • You could start with a NumberFormatter, for example...

    let formatter = NumberFormatter()
    formatter.numberStyle = .percent
    formatter.minimumIntegerDigits = 1
    formatter.maximumIntegerDigits = 1
    formatter.maximumFractionDigits = 3
    
    var values =  [1.623,1.614,1.591,1.577,1.600,1.579,1.622]
    values.reverse()
    let percentages = [0] + zip(values, values.dropFirst()).map {
        (old, new) in
        return (100.0 * (new - old) / old)
    }
    
    print(percentages.compactMap { formatter.string(from: NSNumber(value: $0 / 100.0)) })
    

    which will print something like....

    ["0%", "-2.651%", "1.33%", "-1.438%", "0.888%", "1.446%", "0.558%"]
    

    Your string only shows the signal for negative numbers. This is not what OP asked.

    🙄 Heaven forbid the OP might actually have to do "some" work 🤣😉

    print(percentages.map { ($0 >= 0 ? "+" : "") + formatter.string(for: $0 / 100.0)! })
    

    Or, preferably...

    formatter.positivePrefix = "+"
    //....
    print(percentages.compactMap { formatter.string(for: $0 / 100.0) })
    

    which prints...

    ["+0%", "-2.651%", "+1.33%", "-1.438%", "+0.888%", "+1.446%", "+0.558%"]
    

    can you explain a little further if possible with example how the numbers can be fudged?

    The format of the value doesn't seem to match the value your are producing, for example, 0.8877615726062151 would be 0.8%, not 0.009%, so you have a slight customisation, which would best accomplished by "fudging" the values slightly.

    So, again, starting with...

    let formatter = NumberFormatter()
    formatter.numberStyle = .percent
    formatter.minimumIntegerDigits = 1
    formatter.maximumIntegerDigits = 1
    formatter.maximumFractionDigits = 3
    formatter.minimumFractionDigits = 3
    formatter.positivePrefix = "+"
    

    I modified

    print(percentages.compactMap { formatter.string(for: $0 / 100.0) })
    

    to

    print(percentages.compactMap { formatter.string(for: $0 / 10000.0) })
    

    which now prints

    ["+0.000%", "-0.027%", "+0.013%", "-0.014%", "+0.009%", "+0.014%", "+0.006%"]
    

    which based on my observations, seems to be what you're aiming for - you just get localisation for free