Swift: Distance formatting - ios

I need to convert distances in meters to kilometers.
For example I have the following array of meters:
[10, 100, 5000, 20000, 180000, 180500, 1800000]
Now I want to have these values in kilometers with minimum 3 digits overall. But only if the distance is smaller than 100 km.
[0,01 ; 0,10 ; 5,00 ; 20,0 ; 180 ; 180 ; 1800]
I have tried this with the NumberFormatter:
let numberFormatter = NumberFormatter()
numberFormatter.minimumIntegerDigits = 1
numberFormatter.minimumFractionDigits = 2
numberFormatter.maximumFractionDigits = 2
Do I need therefor a IF/ELSE or can i achieve this with the normal NumberFormatter?
Thanks in advance.

Maybe this is what you want:
let a = [10, 100, 5_000, 20_000, 180_000, 180_500, 1_800_000]
let maxIntegerDigits = Int(log(Float80(Int.max)) + 1)
let formatter = NumberFormatter()
formatter.minimumIntegerDigits = 1
let result: [String] = a.map { distance in
let inKilometers = Double(distance) / 1000
if distance < 10_000 {
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
formatter.maximumIntegerDigits = 1
}
else if distance < 100_000 {
formatter.minimumFractionDigits = 1
formatter.maximumFractionDigits = 1
formatter.maximumIntegerDigits = 2
} else {
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = 0
formatter.maximumIntegerDigits = maxIntegerDigits
}
return formatter.string(for: inKilometers) ?? ""
}
print(result) //["0.01", "0.10", "5.00", "20.0", "180", "180", "1,800"]
You can set the locale of the number formatter in order to change the grouping and decimal separators.

If you want to use NumberFormatter for this, you can create method which returns number of fraction digits which you want to have for your number
private func getNumberOfFractionDigits(for number: Double) -> Int {
switch self {
case ..<10:
return 2
case ..<100:
return 1
default:
return 0
}
}
then just set maximumFractionDigits property of formatter to result of getFractionDigits for your number divided by thousand
let dividedNumber = yourNumber / 1000
numberFormatter.numberStyle = .decimal
numberFormatter.maximumFractionDigits = getNumberOfFractionDigits(for: dividedNumber)
let formatedNumber = Double(numberFormatter.string(from: NSNumber(value: dividedNumber))!)!

Related

Currency NumberFormatter - omit fraction part when the number is integer

I need a specific behaviour from my NumberFormatter used to output currency:
If the number is integer (0, 0.00) it should not show decimal separator (0 €)
Else (123.90, 12.1), it should show two digits after decimal separator (123.90 €, 12.10 €).
The way I create and use my formatter now is the following:
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.currencySymbol = "€"
formatter.alwaysShowsDecimalSeparator = false
let num = formatter.string(from: 123.9)!
If created like this, the formatter always shows decimal separator, despite the fact that I set this property to false.
How can I achieve this?
Currency numberStyle is always returning decimal point followed two digits. So if you want to achieve your goal, you should modify the output string by yourself.
Please check the example code:
let number1: Double = 123.908392857
let number2: Int = 123
let number3: Float = 123.00
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.currencySymbol = "€"
let num1 = formatter.string(from: NSNumber(value: number1))! // Output €123.91
let num2 = formatter.string(from: NSNumber(value: number2))! // Output €123.00
let num3 = formatter.string(from: NSNumber(value: number3))! // Output €123.00
print("\(num1) \(num2) \(num3)")
// This is trick
let newNum1 = trimString(string: num1) // Output €123.91
let newNum2 = trimString(string: num2) // Output €123
let newNum3 = trimString(string: num3) // Output €123
print("\(newNum1) \(newNum2) \(newNum3)")
trimString is a simple trim function, you can put it in the String extension or any place you want.
func trimString(string: String) -> String {
if string.hasSuffix(".00") {
return String(string.dropLast(3))
}
else {
return string
}
}
You may have question about why alwaysShowsDecimalSeparator is not working then? It is for .decimal numberStyle and default decimalSeparator is "."

NumberFormatter wrong result for 0.9972

I have a double:
let value = 0.99720317490866084
And a Double extention function:
extension Double {
func stringWithFixedFractionDigits(min: Int, max: Int) -> String {
let formatter = NumberFormatter()
formatter.minimumFractionDigits = min
formatter.maximumFractionDigits = max
formatter.minimumIntegerDigits = 1
let numberObject = NSNumber(value: self)
return formatter.string(from: numberObject) ?? "\(self)"
}
}
If I use:
value.stringWithFixedFractionDigits(min: 2, max: 2)
I get 1.00 but I would like to get 0.99
What can I change?
You just need to set your NumberFormatter rounding mode property to .down:
formatter.roundingMode = .down
Note that you don't need to create a new NSNumber object, you can safely cast from Double to NSNumber or use string(for: Any) method instead. As an alternative you can extend the protocol FloatingPoint to make it available to all Float types :
extension Formatter {
static let number = NumberFormatter()
}
extension FloatingPoint {
func formattedWithFractionDigits(minimum: Int = 2, maximum: Int = 2, minimumIntegerDigits: Int = 1, roundingMode: NumberFormatter.RoundingMode = .halfEven) -> String {
Formatter.number.roundingMode = roundingMode
Formatter.number.minimumFractionDigits = minimum
Formatter.number.maximumFractionDigits = maximum
Formatter.number.minimumIntegerDigits = minimumIntegerDigits
return Formatter.number.string(for: self) ?? ""
}
}
0.9972031749.formattedWithFractionDigits() // 1.00
0.9972031749.formattedWithFractionDigits(roundingMode: .down) // "0.99"
0.9972031749.formattedWithFractionDigits(minimumIntegerDigits: 0, roundingMode: .down) // ".99"

How can I format currency depending on decimal value?

I am using NSDecimalNumber to format currency and want the following inputs and outputs:
9.99 --> 9.99
10 --> 10
10.00 --> 10
9.90 --> 9.90
9.9 --> 9.90
0 --> 0
0.01 --> 0.01
20 --> 20
10.01 --> 10.01
How can I do this in Swift.
EDIT: Essentially if there are cents (i.e. cents > 0) then display the cents. Otherwise, don't.
Your rule is "Display two fractional digits if either is non-zero; otherwise, display no fractional digits and no decimal point”. I would do it in the most straightforward way:
let number = NSDecimalNumber(string: "12345.00")
let formatter = NSNumberFormatter()
formatter.positiveFormat = "0.00"
let formattedString = formatter.stringFromNumber(number)!
.stringByReplacingOccurrencesOfString(".00", withString: "")
You can use NSNumberFormatter's currency formatting for this. However, there doesn't seem to be a built-in way to do rounding the way you want. Here's a workaround:
let formatter = NSNumberFormatter()
formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
func numToCurrency (num: Double) -> String {
if floor(num) == num {
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = 0
}
else {
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
}
return formatter.stringFromNumber(num)!
}
numToCurrency(9) // "$9"
numToCurrency(9.9) // "$9.90"
Check the NSNumberFormatter class reference for further configuration options (you might need to set a locale for this formatter to automatically use the correct international currency sign for the current user).
(Answering here, as a closed question was re-directed to this one...)
Perhaps the most straightforward route, particularly since this is tagged "Swift", is to determine if it's a whole number or not:
if value.truncatingRemainder(dividingBy: 1) == 0 {
// it's a whole number,
// so format WITHOUT decimal places, e.g. $12
} else {
// it's a fraction,
// so format WITH decimal places, e.g. $12.25
}
the added benefit is avoiding issues with locales and currency formats... no search/replace of ".00" when you're in Germany, for example, where the format is ",00"
edit/update: Xcode 8.3 • Swift 3.1
extension Formatter {
static let noFractionDigits: NumberFormatter = {
let formatter = NumberFormatter()
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = 0
formatter.minimumIntegerDigits = 1
return formatter
}()
static let twoFractionDigits: NumberFormatter = {
let formatter = NumberFormatter()
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
formatter.minimumIntegerDigits = 1
return formatter
}()
}
extension FloatingPoint {
var customDescription: String {
return rounded(.down) == self ?
Formatter.noFractionDigits.string(for: self) ?? "" :
Formatter.twoFractionDigits.string(for: self) ?? ""
}
}
extension String {
var double: Double { return Double(self) ?? 0 }
}
let array = ["9.99","10","10.00","9.90","9.9"]
let results = array.map { $0.double.customDescription }
results // ["9.99", "10", "10", "9.90", "9.90"]
Here's how to create a custom formatter class to handle this for you:
import Foundation
class CustomFormatter: NSNumberFormatter {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init() {
super.init()
self.locale = NSLocale.currentLocale()
self.numberStyle = .DecimalStyle
}
func isIntegerNumber(number:NSNumber) -> Bool {
var value: NSDecimal = number.decimalValue
if NSDecimalIsNotANumber(&value) { return false }
var rounded = NSDecimal()
NSDecimalRound(&rounded, &value, 0, NSRoundingMode.RoundPlain)
return NSDecimalCompare(&rounded, &value) == NSComparisonResult.OrderedSame
}
override func stringFromNumber(number: NSNumber) -> String? {
if isIntegerNumber(number) {
self.minimumFractionDigits = 0
self.maximumFractionDigits = 0
return super.stringFromNumber(number)
}
else {
self.minimumFractionDigits = 2
self.maximumFractionDigits = 2
return super.stringFromNumber(number)
}
}
}
let formatter = CustomFormatter()
formatter.stringFromNumber(NSDecimalNumber(double: 5.00)) // -> "5"
formatter.stringFromNumber(NSDecimalNumber(double: 5.01)) // -> "5.01"
formatter.stringFromNumber(NSDecimalNumber(double: 5.10)) // -> "5.10"
Thanks to this post for the proper way to test if a NSDecimal is an integer.
I think it's best to let the currencyStyle determine the maximumFractionDigits. Just set the minimumFractionDigits to 0 where desired. The code is slightly shorter, but as a bonus if you set the locale, this way will allow for languages that don't have 2 decimal places.
Using NSNumberFormatter gives you the benefit of currency symbols, decimal places and comma’s, all in the perfect places for the different locale’s.
extension NSNumber {
func currencyString() -> String? {
let formatter = NSNumberFormatter()
formatter.numberStyle = .CurrencyStyle
if self.isEqualToNumber(self.integerValue) {
formatter.minimumFractionDigits = 0
}
return formatter.stringFromNumber(self)
}
}
let inputArray: [NSDecimalNumber] = [9.99, 10, 10.00, 9.90, 0, 0.01, 20, 10.01, 0.5, 0.055, 5.0]
let outputArray: [String] = inputArray.map({return $0.currencyString() ?? "nil"})
print(outputArray)
["$9.99", "$10", "$10", "$9.90", "$0", "$0.01", "$20", "$10.01", "$0.50", "$0.06", "$5"]
Adding a locale to a NSNumberFormatter looks like this(ex. from an SKProduct object):
formatter.locale = product!.priceLocale
For an OSX app you need to add:
formatter.formatterBehavior = .Behavior10_4

Swift - Remove Trailing Zeros From Double

What is the function that removes trailing zeros from doubles?
var double = 3.0
var double2 = 3.10
println(func(double)) // 3
println(func(double2)) // 3.1
You can do it this way but it will return a string:
var double = 3.0
var double2 = 3.10
func forTrailingZero(temp: Double) -> String {
var tempVar = String(format: "%g", temp)
return tempVar
}
forTrailingZero(double) //3
forTrailingZero(double2) //3.1
In Swift 4 you can do it like that:
extension Double {
func removeZerosFromEnd() -> String {
let formatter = NumberFormatter()
let number = NSNumber(value: self)
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = 16 //maximum digits in Double after dot (maximum precision)
return String(formatter.string(from: number) ?? "")
}
}
example of use: print (Double("128834.567891000").removeZerosFromEnd())
result: 128834.567891
You can also count how many decimal digits has your string:
import Foundation
extension Double {
func removeZerosFromEnd() -> String {
let formatter = NumberFormatter()
let number = NSNumber(value: self)
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = (self.components(separatedBy: ".").last)!.count
return String(formatter.string(from: number) ?? "")
}
}
Removing trailing zeros in output
This scenario is good when the default output precision is desired. We test the value for potential trailing zeros, and we use a different output format depending on it.
extension Double {
var stringWithoutZeroFraction: String {
return truncatingRemainder(dividingBy: 1) == 0 ? String(format: "%.0f", self) : String(self)
}
}
(works also with extension Float, but not Float80)
Output:
1.0 → "1"
0.1 → "0.1"
0.01 → "0.01"
0.001 → "0.001"
0.0001 → "0.0001"
Formatting with maximum fraction digits, without trailing zeros
This scenario is good when a custom output precision is desired.
This solution seems roughly as fast as NumberFormatter + NSNumber solution from MirekE, but one benefit could be that we're avoiding NSObject here.
extension Double {
func string(maximumFractionDigits: Int = 2) -> String {
let s = String(format: "%.\(maximumFractionDigits)f", self)
for i in stride(from: 0, to: -maximumFractionDigits, by: -1) {
if s[s.index(s.endIndex, offsetBy: i - 1)] != "0" {
return String(s[..<s.index(s.endIndex, offsetBy: i)])
}
}
return String(s[..<s.index(s.endIndex, offsetBy: -maximumFractionDigits - 1)])
}
}
(works also with extension Float, but not Float80)
Output for maximumFractionDigits: 2:
1.0 → "1"
0.12 → "0.12"
0.012 → "0.01"
0.0012 → "0"
0.00012 → "0"
Note that it performs a rounding (same as MirekE solution):
0.9950000 → "0.99"
0.9950001 → "1"
In case you're looking how to remove trailing zeros from a string:
string.replacingOccurrences(of: "^([\d,]+)$|^([\d,]+)\.0*$|^([\d,]+\.[0-9]*?)0*$", with: "$1$2$3", options: .regularExpression)
This will transform strings like "0.123000000" into "0.123"
All the answers i found was good but all of them had some problems like producing decimal numbers without the 0 in the beginning ( like .123 instead of 0.123). but these two will do the job with no problem :
extension Double {
func formatNumberWithFixedFraction(maximumFraction: Int = 8) -> String {
let stringFloatNumber = String(format: "%.\(maximumFraction)f", self)
return stringFloatNumber
}
func formatNumber(maximumFraction: Int = 8) -> String {
let formatter = NumberFormatter()
let number = NSNumber(value: self)
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = maximumFraction
formatter.numberStyle = .decimal
formatter.allowsFloats = true
let formattedNumber = formatter.string(from: number).unwrap
return formattedNumber
}
}
The first one converts 71238.12 with maxFraction of 8 to: 71238.12000000
but the second one with maxFraction of 8 converts it to: 71238.12
This one works for me, returning it as a String for a text label
func ridZero(result: Double) -> String {
let value = String(format: "%g", result)
return value
}
Following results
ridZero(result: 3.0) // "3"
ridZero(result: 3.5) // "3.5"

iOS Swift: Eliminate trailing zeroes for CGFloat

I have float value that ends in .0 or .5 (for example 10.0 or 10.5). If it ends in .0 I would like to eliminate the trailing zero. What is the easiest way to do this in Swift?
func myFunction() {
var string = pickerData[picker.selectedRowInComponent(0)]
var float = ((string as NSString).floatValue - 45) / 2
label.text = float.description
}
You should use NSNumberFormatter() to format your numbers as follow:
extension Double {
var formatted:String {
let formatter = NSNumberFormatter()
formatter.numberStyle = NSNumberFormatterStyle.DecimalStyle
// you can set the minimum fraction digits to 0
formatter.minimumFractionDigits = 0
// and set the maximum fraction digits to 1
formatter.maximumFractionDigits = 1
return formatter.stringFromNumber(self) ?? ""
}
}
10.5.formatted // "10.5"
10.0.formatted // "10"
In your case it would look like this:
label.text = float.formatted

Resources