Swift String: append 0s after decimal separator - swift

I need to append 0, 1, or 2 0s to a string, depends on its decimal separator, so that
"100", "100." and "100.0" becomes "100.00"
"100.8" becomes "100.80"
"100.85" remains unchanged
I could find the decimal separator and check its distance to end endIndex of the string, but is there an easier way of doing it?
NumberFormatter does this, but the actual string I have, isn't a plain number that can go through a formatter.
For example:
let amount = "123,456,789"
then formatted amount should be "123,456,789.00"
assumption:
the given string has at most one decimal separator with at most two decimal places
So there can't be string like: "123.4.4.5"
Also I want to use the decimal separator from NumberFormatter().decimalSeparator

You could pass the string through a decimal formatter to get the underlying number, and then back again through the formatter to get a formatted string:
let amount = "123,456,789"
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 2
let number = formatter.number(from: amount)
let newAmountString = formatter.string(from: number!) //"123,456,789.00"
(You should check that number is not nil before force unwrapping it, with if letor guard)
You could wrap this in a function:
func zeroPadding(toString: String) -> String? {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 2
guard let number = formatter.number(from: toString) else {
return nil
}
return formatter.string(from: number)
}
Here are some test cases:
zeroPadding(toString: "123,456,789") //"123,456,789.00"
zeroPadding(toString: "123,456,789.0") //"123,456,789.00"
zeroPadding(toString: "123,456,789.10") //"123,456,789.10"
zeroPadding(toString: "123,456,789.123") //"123,456,789.12"
zeroPadding(toString: "123.4567") //"123.46"
zeroPadding(toString: "Price: 1€ for a 💩") //nil
Or define it as an extension on String:
extension String {
func withZeroPadding() -> String? {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 2
guard let number = formatter.number(from: self) else {
return nil
}
return formatter.string(from: number)
}
}
And use it like this:
"123.4.4.5".withZeroPadding() //nil
"12.".withZeroPadding() //"12.00"
"123,456,789".withZeroPadding() //"123,456,789.00"

This is the following code snippet I have tested on Playground, it can be achieved more smartly but for now it is working.
//let amount = "123,456,789.545222323"
//let amount = "123,456,789."
let amount = "123,456,789"
let removeSpaces = amount.replacingOccurrences(of: " ", with: "")
if removeSpaces.count > 0
{
let arrSTR = removeSpaces.components(separatedBy: ".")
if arrSTR.count > 1
{
var strAfterDecimal = arrSTR[1]
if strAfterDecimal.count >= 2
{
strAfterDecimal = strAfterDecimal[0..<2]
}else if strAfterDecimal.count != 0
{
strAfterDecimal = "\(strAfterDecimal)0"
}else
{
strAfterDecimal = "00"
}
let finalSTR = String("\(arrSTR[0]).\(strAfterDecimal)")
print("Final with Decimal - \(finalSTR)")
}else
{
let finalSTR = String(arrSTR[0] + ".00")
print("Final without Decimal - \(finalSTR)")
}
}
extension String {
subscript(_ range: CountableRange<Int>) -> String {
let idx1 = index(startIndex, offsetBy: max(0, range.lowerBound))
let idx2 = index(startIndex, offsetBy: min(self.count, range.upperBound))
return String(self[idx1..<idx2])
}
}

Related

How to convert numbers to letters in swift?

I'm stuck help. You have to use strings, but I haven't really found a logical way to make this work well.
var letters = ["Zero","One","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten"]
var str:String
print("Enter a number from 0 to 10: ")
str = readLine()!
print (letters[0])
...
print("Enter a number from 0 to 10: ")
str = readLine()!
print (letters[10])
Maybe this api is similar but not exactly what you need.
https://developer.apple.com/documentation/foundation/numberformatter/style/spellout
let formatter: NumberFormatter = {
let nf = NumberFormatter()
nf.numberStyle = .spellOut
nf.locale = Locale(identifier: "en_US")
return nf
}()
extension Numeric {
var spelledOut: String? {
return formatter.string(for: self)
}
}
let one = 1.spelledOut
print(one) //->one
print(25.spelledOut) //->twenty-five
print(1.5.spelledOut) //->one point five
https://developer.apple.com/forums/thread/121448
You can do it like this with String extension
extension String {
func wordToInteger() -> Int {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .spellOut
return numberFormatter.number(from: self.lowercased()) as? Int ?? 0
}
}
Use like this
let letters = ["Zero","One","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten"]
print(letters[10].wordToInteger()) // In your Case
print("Three".wordToInteger())
Output

Second decimal place gets truncated if it is 0

I'm new to Swift, so bear with me. :)
I'm having trouble showing the output currency to two decimal places. Currently, it only shows one decimal place. For example, if I input $1.10, the output is $1.1.
However, if I input $1.11, the output is still $1.11.
func currencyInputDoubling() -> String {
var number: NSNumber!
let formatter = NumberFormatter()
formatter.numberStyle = .currencyAccounting
formatter.currencySymbol = CurrencyManager.shared.currentCurrency.sign
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 2
var amountWithPrefix = self
// remove from String: "$", ".", ","
let regex = try! NSRegularExpression(pattern: "[^0-9]", options: .caseInsensitive)
amountWithPrefix = regex.stringByReplacingMatches(in: amountWithPrefix, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, self.count), withTemplate: "")
let double = (amountWithPrefix as NSString).doubleValue
number = NSNumber(value: (double / 100))
// if first number is 0 or all numbers were deleted
guard number != 0 as NSNumber else {
return ""
}
return "\(double / 100)"
}
I would suggest using Locale instead of currencySymbol and to create static number formatters that can be reused.
let currencyFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.locale = .current
return formatter
}()
let numberFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.locale = .current
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
return formatter
}()
And then the method can be simplified as
func currencyInputDoubling(_ amountWithPrefix: String) -> String {
guard let value = currencyFormatter.number(from: amountWithPrefix) else { return "" }
return numberFormatter.string(from: value) ?? ""
}
If you need to set Locale to something else than .current you could pass it as an argument
func currencyInputDoubling(_ amountWithPrefix: String, using locale: Locale) -> String {
currencyFormatter.locale = locale
numberFormatter.locale = locale
guard let value = currencyFormatter.number(from: amountWithPrefix) else { return "" }
return numberFormatter.string(from: value) ?? ""
}
I would suggest, at a minimum, using the formatter you created, rather than doing string interpolation. When you return "\(double / 100)", using simple string interpolation, that can’t avail itself of the formatter’s fractional digits setting.
Perhaps:
func currencyInputDoubling() -> String {
let formatter = NumberFormatter()
formatter.numberStyle = .currencyAccounting
formatter.currencySymbol = CurrencyManager.shared.currentCurrency.sign
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 2
// remove from String: "$", ".", ","
let digitsOnly = filter("0123456789".contains)` // or, if you want to use regex, simply `let digitsOnly = replacingOccurrences(of: "[^0-9]", with: "", options: .regularExpression)`
// return formatted string
guard let value = Double(digitsOnly) {
return ""
}
return formatter.string(for: value / 100) ?? ""
}

iOS Swift - Convert double to String precision last value rounded to next value

Example value : 3.00035358. i am trying to convert double value to string
Method 1:
let num = NSNumber(value:self)
let formatter : NumberFormatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.minimumFractionDigits = 4
let str = formatter.string(from: num)!
return str
method2 :
extension Double {
var stringWithoutZeroFraction: String {
return truncatingRemainder(dividingBy: 1) == 0 ? String(d: "%.0f", self) : String(format:"%.4f", self)
}
}
expecting output to be 3.003 but getting like 3.004. i do want my last digit to be rounded to next digit.how to fix tho issue.any help will be appricated.thanks in advance
If you use a NumberFormatter, you need to set the roundingMode to .floor to achieve truncating. Also, if you want to truncate, you probably want to set maximumFractionDigits instead of minimumFractionDigits.
extension Double {
var string: String {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 4
formatter.roundingMode = .floor
return formatter.string(for: self) ?? description
}
}
3.00035358.string // "3.0003"

Converting text field data to decimal value and back to string

I converted text field string into a double to do calculations and then back to a string to output it on a label. I am now working with currency inputs so I need to convert it to a decimal rather than a double. Can someone help?
func calcTotal() {
let totalConv: Double? = Double(totalTextField.text!)
let tipConv: Double? = Double(tipTextField.text!)
guard totalConv != nil && tipConv != nil else {
return
}
let result = totalConv! * ((tipConv! / 100) + 1)
let output = String(format: "$ %.2f", result)
totalAmount.text = String(output)
}
You will just need to use Decimal(string:) initializer and NumberFormatter (currency style) to format your decimal value.
func calcTotal() {
guard
let totalConv = Decimal(string: totalTextField.text!),
let tipConv = Decimal(string: tipTextField.text!)
else { return }
let result = totalConv * ((tipConv / 100) + 1)
totalAmount.text = Formatter.currency.string(for: result)
}
extension Formatter {
static let currency: NumberFormatter = {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .currency
return numberFormatter
}()
}

Converting String to Currency Swift

I am having trouble converting my array of String to Currency.
I have created an extension currencyInputFormatting(). However, the commas are being placed in the wrong spots.
Here is my code :-
cell.balanceLabel.text? = (monthlyBalanceStringArray)[indexPath.row].currencyFormatting()
extension String {
// formatting text for currency textField
func currencyFormatting() -> String {
var number: NSNumber!
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.maximumFractionDigits = 2
var amountWithPrefix = self
let regex = try! NSRegularExpression(pattern: "[^0-9]", options: .caseInsensitive)
amountWithPrefix = regex.stringByReplacingMatches(in: amountWithPrefix, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, self.characters.count), withTemplate: "")
let double = (amountWithPrefix as NSString).doubleValue
number = NSNumber(value: (double))
// number = NSNumber(value: (double / 100))
guard number != 0 as NSNumber else {
return ""
}
return formatter.string(from: number)!
}
}
You don't need to replace any any characters using regex. Just use NSNumberFormatter
extension String {
// formatting text for currency textField
func currencyFormatting() -> String {
if let value = Double(self) {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 2
if let str = formatter.string(for: value) {
return str
}
}
return ""
}
}
"74154.7".currencyFormatting() // $74,154.70
"74719.4048014544".currencyFormatting() // $74,719.40
You can use following function for valid currency format -
extension Int {
func createCurrencyString() -> String {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.maximumFractionDigits = 0
return formatter.string(from: NSNumber(value: self))!
}
}