Swift playground - How to convert a string with comma to a string with decimal - swift

I'm new in the Swift world.
How can I converting a String with a comma to a String with a decimal?
The code work's fine with a dot (.)
The problem is when I'm using a comma (,) ... with: var price
The origin of the problem is the Decimal french keyboard use a comma (,) instead of a dot (.)
Don't know exactly how to use NSNumberFormatter or generatesDecimalNumbers if it's the key. There's probebly more than one options.
//The answer change if "2,25" or "2.25" is used.
var price : String = "2,25"
var priceFloat = (price as NSString).floatValue
//I need to have 2.25 as answer.
var costString = String(format:"%.2f", priceFloat)
Thank's for your time and your help!

update: Xcode 8.2.1 • Swift 3.0.2
You can use NumberFormatter() to convert your string to number. You just need to specify the decimalSeparator as follow:
extension String {
static let numberFormatter = NumberFormatter()
var doubleValue: Double {
String.numberFormatter.decimalSeparator = "."
if let result = String.numberFormatter.number(from: self) {
return result.doubleValue
} else {
String.numberFormatter.decimalSeparator = ","
if let result = String.numberFormatter.number(from: self) {
return result.doubleValue
}
}
return 0
}
}
"2.25".doubleValue // 2.25
"2,25".doubleValue // 2.25
let price = "2,25"
let costString = String(format:"%.2f", price.doubleValue) // "2.25"
You should do the currency formatting also with NumberFormat, so create a read-only computed property currency extending FloatingPoint protocol to return a formatted string from the String doubleValue property.
extension NumberFormatter {
convenience init(style: Style) {
self.init()
self.numberStyle = style
}
}
extension Formatter {
static let currency = NumberFormatter(style: .currency)
}
extension FloatingPoint {
var currency: String {
return Formatter.currency.string(for: self) ?? ""
}
}
let costString = "2,25".doubleValue.currency // "$2.25"
Formatter.currency.locale = Locale(identifier: "en_US")
"2222.25".doubleValue.currency // "$2,222.25"
"2222,25".doubleValue.currency // "$2,222.25"
Formatter.currency.locale = Locale(identifier: "pt_BR")
"2222.25".doubleValue.currency // "R$2.222,25"
"2222,25".doubleValue.currency // "R$2.222,25"

You can use for this Swift 3:
let currentAmount = "2,50"
currentAmount = currentAmount.replacingOccurrences(of: ",", with: ".")
print(currentAmount) // "2.50\n"

var price = "2,25"
price = price.replacingOccurrences(of: ",", with: ".")
var priceFloat = (price as NSString).floatValue

Nullable extension version:
extension String
{
static let customNumberFormatter = NumberFormatter()
var doubleValue: Double? {
String.customNumberFormatter.decimalSeparator = "."
if let result = String.customNumberFormatter.number(from: self) {
return result.doubleValue
} else {
String.customNumberFormatter.decimalSeparator = ","
if let result = String.customNumberFormatter.number(from: self) {
return result.doubleValue
}
}
return nil
}
}

EDIT: Updated to work with the current version of Swift:
let amount = "8,35"
var counter: Int = 0
var noCommaNumber: String!
for var carattere in (amount) {
if carattere == "," { carattere = "." }
if counter != 0 { noCommaNumber = "\(noCommaNumber ?? "\(carattere)")" + "\(carattere)" } else { noCommaNumber = "\(carattere)" } // otherwise first record will always be nil
counter += 1
}
let importo = Float(noCommaNumber)

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

How to Format Number Converted From String Without Losing Decimal Precision

I have the following JSON payload that I need to convert to numbers and subsequently format for display.
{
"kilometers_per_second": "14.4578929636",
"kilometers_per_hour": "52048.4146691173",
"miles_per_hour": "32340.8607703746"
}
Using Codable, I created the following structure:
struct RelativeVelocity: Codable, Equatable {
let kilometersPerSecond: String?
let kilometersPerHour: String?
let milesPerHour: String?
enum CodingKeys: String, CodingKey {
case kilometersPerSecond = "kilometers_per_second"
case kilometersPerHour = "kilometers_per_hour"
case milesPerHour = "miles_per_hour"
}
}
The properties are String instances because that's what the API returns, and I am learning to use view models for the first time, so I would like to use a view model to convert the String instances into numbers prior to returning formatted String instances.
My view model has the following structure:
struct RelativeVelocityViewModel {
private let relativeVelocity: RelativeVelocity
init(relativeVelocity: RelativeVelocity) {
self.relativeVelocity = relativeVelocity
}
}
extension RelativeVelocityViewModel {
var formattedKilometersPerHour: String? {
guard
let stringValue = relativeVelocity.kilometersPerHour,
let decimalValue = Decimal(string: stringValue),
let formatted = NumberFormatter.relativeVelocityFormatter.string(from: decimalValue as NSNumber)
else { return nil }
return formatted
}
var formattedKilometersPerSecond: String? {
guard
let stringValue = relativeVelocity.kilometersPerSecond,
let decimalValue = Decimal(string: stringValue),
let formatted = NumberFormatter.relativeVelocityFormatter.string(from: decimalValue as NSNumber)
else { return nil }
return formatted
}
var formattedMilesPerHour: String? {
guard
let stringValue = relativeVelocity.kilometersPerSecond,
let decimalValue = Decimal(string: stringValue),
let formatted = NumberFormatter.relativeVelocityFormatter.string(from: decimalValue as NSNumber)
else { return nil }
return formatted
}
}
As you can see, it converts the String instances into Decimal instances, and the Decimal instances are then formatted by the following NumberFormatter:
extension NumberFormatter {
static let relativeVelocityFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.maximumFractionDigits = .max
formatter.numberStyle = .decimal
formatter.usesGroupingSeparator = true
return formatter
}()
}
My XCTestCase subclass for testing my view models is:
class Tests_RelativeVelocityViewModel: XCTestCase {
let kilometersPerSecond = "14.4578929636"
let kilometersPerHour = "52048.4146691173"
let milesPerHour = "32340.8607703746"
var populatedViewModel: RelativeVelocityViewModel!
var emptyViewModel: RelativeVelocityViewModel!
override func setUpWithError() throws {
try super.setUpWithError()
let populatedRelativeVelocity = RelativeVelocity(
kilometersPerSecond: kilometersPerSecond,
kilometersPerHour: kilometersPerHour,
milesPerHour: milesPerHour
)
populatedViewModel = RelativeVelocityViewModel(relativeVelocity: populatedRelativeVelocity)
let emptyRelativeVelocity = RelativeVelocity(
kilometersPerSecond: nil,
kilometersPerHour: nil,
milesPerHour: nil
)
emptyViewModel = RelativeVelocityViewModel(relativeVelocity: emptyRelativeVelocity)
}
override func tearDownWithError() throws {
emptyViewModel = nil
populatedViewModel = nil
try super.tearDownWithError()
}
func test_RelativeVelocityViewModel_ReturnsNilFormattedKilometersPerHour_WhenValueIsMissing() {
XCTAssertNil(emptyViewModel.formattedKilometersPerHour)
}
func test_RelativeVelocityViewModel_ReturnsFormattedKilometersPerHour_WhenValueIsPresent() {
let expected = "52,048.4146691173"
XCTAssertEqual(populatedViewModel.formattedKilometersPerHour, expected)
}
func test_RelativeVelocityViewModel_ReturnsNilFormattedKilometersPerSecond_WhenValueIsMissing() {
XCTAssertNil(emptyViewModel.formattedKilometersPerSecond)
}
func test_RelativeVelocityViewModel_ReturnsNilFormattedMilesPerHour_WhenValueIsMissing() {
XCTAssertNil(emptyViewModel.formattedMilesPerHour)
}
}
The following test...
func test_RelativeVelocityViewModel_ReturnsFormattedKilometersPerHour_WhenValueIsPresent() {
let expected = "52,048.4146691173"
XCTAssertEqual(populatedViewModel.formattedKilometersPerHour, expected)
}
...produces the following failure:
XCTAssertEqual failed: ("Optional("52,048.414669")") is not equal to ("Optional("52,048.4146691173")")
I know that I can use XCTAssertEqual(_:_:accuracy:_:file:line:), but I want to retain all of the decimal values.
What am I doing incorrectly that is causing the formatted result to be rounded by losing the value's precision?
Try this:
class MyProjectTests: XCTestCase {
func testExample() throws {
let stringValue = "52048.12345678911111"
let decimalValue = Decimal(string: stringValue)!
let formatted = NumberFormatter.relativeVelocityFormatter(maxFractionDigits: decimalValue.significantFractionalDecimalDigits).string(from: decimalValue as NSNumber)
XCTAssert(formatted == stringValue)
}
}
extension NumberFormatter {
static func relativeVelocityFormatter(maxFractionDigits: Int) -> NumberFormatter {
let formatter = NumberFormatter()
formatter.maximumFractionDigits = maxFractionDigits
formatter.numberStyle = .none
formatter.usesGroupingSeparator = true
return formatter
}
}
extension Decimal {
var significantFractionalDecimalDigits: Int {
return max(-exponent, 0)
}
}
Anyway, there is always a limit:
33 decimal digits.

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
}()
}

What's wrong with my code? It says unresolved Identifier

It says "use of unresolved identifier" for both NSNumber and NSNumber. I can't find a solution for this.
I should be able to create a program that will convert each number from a given integer to its equivalent word value. The accepted value should be UInt16 only
public extension Int {
public var asWord: String {
let numberValue = NSNumber(value: self)
let formatter = NSNumberFormatter()
formatter.numberStyle = .spellOut
return formatter.string(from: numberValue)!
}
}
var value = 2500
if value > UInt16.max || value < UInt16.min {
print("The accepted value should be UInt16 only")
}
else {
print("\(value.asWord)")
}
You forgot import Foundation.
Try this (Swift 4):
import Foundation
public extension Int {
public var asWord: String {
let numberValue = NSNumber(value: self)
let formatter = NumberFormatter()
formatter.numberStyle = .spellOut
return formatter.string(from: numberValue)!
}
}
var value = 2500
if value > UInt16.max || value < UInt16.min{
print("The accepted value should be UInt16 only")
} else {
print("\(value.asWord)")
}
Do you use the if block and value definition in a class?!!
like this:
public extension Int {
public var asWord: String {
let numberValue = NSNumber(value: self)
let formatter = NSNumberFormatter()
formatter.numberStyle = .spellOut
return formatter.string(from: numberValue)!
}
}
class yourClassName {
var value = 2500
if value > UInt16.max || value < UInt16.min {
print("The accepted value should be UInt16 only")
}
else {
print("\(value.asWord)")
}
}

Check the language of numbers if it is English or Arabic/Persian

In my project I am planning to change the language of any type number to Persian and here is what I have done:
public extension String {
func perstianString (string: String)->String {
let digitSet = CharacterSet.decimalDigits
var finalString = String()
for ch in string.unicodeScalars {
if digitSet.contains(ch) {
let sh = convertoPersianNum(num: "\(ch)")
finalString += sh
}
} else {
finalString += "\(ch)"
}
}
return finalString
}
func convertoPersianNum(num: String)->String{
var retVlue = String()
var num1 = num
let number = NSNumber(value: Int(num1)!)
let numb = (num as NSString).intValue
let format = NumberFormatter()
format.locale = Locale(identifier: "fa_IR")
let faNumber = format.string(from: number)
return faNumber!
}
}
But when the source value has Persian numbers, the app crashes. Simply said, I want to check if it is a Persian number, don't do anything, else do the the conversion above:
let string = "ییسس ۱۲۳۴"
with this type do not do anything else do something.
public extension String {
private var isPersian: Bool {
let predicate = NSPredicate(format: "SELF MATCHES %#", "(?s).*\\p{Arabic}.*")
return predicate.evaluate(with: self)
}
private var toPersianNum: String {
let number = NSDecimalNumber(string: self)
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: "fa_IR")
return formatter.string(from: number) ?? ""
}
var persian:String {
var retVal = String()
self.enumerateSubstrings(in: startIndex..<endIndex, options: .byComposedCharacterSequences) { (string, _, _, _) in
guard let string = string else {return}
if string.isPersian {
retVal += string
}else {
retVal += string.toPersianNum
}
}
return retVal
}
}
let string = "9001ییسس2345777 ۱۲۳۴2345"
string.persian // "۹۰۰۱ییسس۲۳۴۵۷۷۷ناعدد۱۲۳۴۲۳۴۵" (result)