Swift 5: Add decimal separators whitout NumberFormatter - ios

I need to create a function to add the decimal and thousands separators to a number that is a string, I know I could use numberFormatter but I want to practice another way of doing it. For example if I have "454685" the result will be "$ 4,546.85".
This is my code:
let number = "111234567"
let firstIndex = number.startIndex
let firstNumber = number[firstIndex]
let lastIndex = number.index(before: number.endIndex)
let lastNumber = String(number[lastIndex])
let beforeLastIndex = number.index(number.endIndex, offsetBy: -2)
let beforeLastNumber = String(number[beforeLastIndex])
var decimalPart = beforeLastNumber + lastNumber
let decimalindex = number.index(number.endIndex, offsetBy: -2)
let intPart = String(number[..<decimalindex])
let totalNumber = "$" + intPart + "," + decimalPart
print(totalNumber)
var thousandSeparators = intPart.count % 3 == 1
I'm stuck here, I separated the decimal part from the integer part, but then I don't know how to add the thousand separator "." to string number.

You can achieve this by creating a function for a string that split the string as per the length passes in parameter and the direction for splitting.
extension String {
func split(length:Int, fromBack: Bool = false) -> [Substring] {
guard length > 0 && length < count else { return [suffix(from:startIndex)] }
if fromBack {
return (0 ... (count - 1) / length).map { dropLast($0 * length).suffix(length) }.reversed()
}
return (0 ... (count - 1) / length).map { dropFirst($0 * length).prefix(length) }
}
}
how to use this function:
let number = "111234567"
let firstIndex = number.startIndex
let firstNumber = number[firstIndex]
let lastIndex = number.index(before: number.endIndex)
let lastNumber = String(number[lastIndex])
let beforeLastIndex = number.index(number.endIndex, offsetBy: -2)
let beforeLastNumber = String(number[beforeLastIndex])
var decimalPart = beforeLastNumber + lastNumber
let decimalindex = number.index(number.endIndex, offsetBy: -2)
let intPart = String(number[..<decimalindex])
let intWithThousandSeparators = intPart.split(length: 3, fromBack: true).joined(separator: ".")
let totalNumber = "$" + intWithThousandSeparators + "," + decimalPart
print(totalNumber)
Output:
You can refer to this answers list for split strings into groups.

Related

How to make a calculation according to the month (each month correponds a value) inserted in a textfield ios - Swift

I have a textfield where the user inserts a specific month with a pickerview. Every month has to correspond to a certain number (which are the working hours in that month).
I need the number for a calculation
here is the code that I wrote:
let redditonetto = Float(reddito.text!) ?? 0
let totaleore = Float(ore.text!) ?? 0
//da reddito 0 a 1247.73
let calcA = redditonetto / 168;
let calcB = totaleore * calcA;
let calcC = calcB * 0.80;
let calcD = calcC - (calcC * 0.0584)
let trat = calcC * 0.0584
If you see let calcA, I need that redditonetto will be divided to a number according to the month inserted in the textfield.
Something like this:
func trat(income: UITextField, hours: UITextField, picker: UIPickerView, monthHours: [Float]) -> Float {
let netIncome = Float(income.text!) ?? 0
let totalHours = Float(hours.text!) ?? 0
let month = picker.selectedRow(inComponent: 0)
guard 0 <= month && month < 12 else { return 0 }
//income 0 to 1247.73
let calcA = netIncome / monthHours[month];
let calcB = totalHours * calcA;
let calcC = calcB * 0.80;
let calcD = calcC - (calcC * 0.0584)
return calcD
}
However, note that this won't work (will return 0) unless a month has been selected.
thank you Daniel T. I really appreciate. I give you my code to understand How to implement and when put return
#IBAction func calcolaPressed(_ sender: UIButton) {
let oremese = ["Gennaio": 184, "Febbraio": 160, "Marzo": 176, "Aprile": 176, "Maggio": 168, "Giugno": 176, "Luglio": 184, "Agosto": 168, "Settembre": 176, "Ottobre": 176, "Novembre": 168, "Dicembre": 184]
func trat(income: UITextField, hours: UITextField, picker: UIPickerView, oremese: [Float]) -> Float {
let redditonetto = Float(reddito.text!) ?? 0
let totaleore = Float(ore.text!) ?? 0
let month = picker.selectedRow(inComponent: 0)
guard 0 <= month && month < 12 else { return 0 }
//da reddito 0 a 1247.73
let calcA = redditonetto / 168;
let calcB = totaleore * calcA;
let calcC = calcB * 0.80;
let calcD = calcC - (calcC * 0.0584)
let trat = calcC * 0.0584
let myDoubleC = calcC
let doubleStrC = (String(format: "%.2f", myDoubleC))
let myDoubleD = calcD
let doubleStrD = (String(format: "%.2f", myDoubleD))
let myDoubleT = trat
let doubleStrT = (String(format: "%.2f", myDoubleT))
//da reddito 1247.74 a 2159.49
let calcB2 = totaleore * 5.67;
let calcC2 = calcB2 - (calcB2 * 0.0584)
let trat2 = calcC2 * 0.0584
let myDoubleB2 = calcB2
let doubleStrB2 = (String(format: "%.2f", myDoubleB2))
let myDoubleC2 = calcC2
let doubleStrC2 = (String(format: "%.2f", myDoubleC2))
let myDoubleT2 = trat2
let doubleStrT2 = (String(format: "%.2f", myDoubleT2))
//da reddito da 2159.50 a 30000
let calcB3 = totaleore * 7.14;
let calcC3 = calcB3 - (calcB3 * 0.0584)
let trat3 = calcC3 * 0.0584
let myDoubleB3 = calcB3
let doubleStrB3 = (String(format: "%.2f", myDoubleB3))
let myDoubleC3 = calcC3
let doubleStrC3 = (String(format: "%.2f", myDoubleC3))
let myDoubleT3 = trat3
let doubleStrT3 = (String(format: "%.2f", myDoubleT3))
if redditonetto >= 0 && redditonetto <= 1247.73 {
nettocig.text = "€ " + "\(doubleStrD)"
indennitalorda.text = "€ " + "\(doubleStrC)"
trattenute.text = "€ " + "\(doubleStrT)"
}
else if redditonetto >= 1247.74 && redditonetto <= 2159.49 {
nettocig.text = "€ " + "\(doubleStrC2)"
indennitalorda.text = "€ " + "\(doubleStrB2)"
trattenute.text = "€ " + "\(doubleStrT2)"
} else {
nettocig.text = "€ " + "\(doubleStrC3)"
indennitalorda.text = "€ " + "\(doubleStrB3)"
trattenute.text = "€ " + "\(doubleStrT3)"
}
if redditonetto >= 0 && redditonetto <= 2159.49{
massimale.text = "€ 998.18"
progressBar.progress = 0.7
}
else { massimale.text = "€ 1199.72"
progressBar.progress = 1
}
}
}
}

Gradually and randomly visualize string

I am currently working on a simple program to gradually and randomly visualize a string in two iteration. Right now I have managed to get the first iteration but I'm not sure how to do the second one. If someone could give any example or advice I would be very grateful. My code looks like this:
let s = "Hello playground"
let factor = 0.25
let factor2 = 0.45
var n = s.filter({ $0 != " " }).count // # of non-space characters
var m = lrint(factor * Double(n)) // # of characters to display
let t = String(s.map { c -> Character in
if c == " " {
// Preserve space
return " "
} else if Int.random(in: 0..<n) < m {
// Replace
m -= 1
n -= 1
return c
} else {
// Keep
n -= 1
return "_"
}
})
print(t) // h_l__ _l_______d
To clarify, I want to use factor2 in the second iteration to print something that randomly add letters on top of t that looks something like this h_l_o pl_g_____d.
Replacing Characters
Starting from #MartinR's code, you should remember the indices that have been replaced. So, I am going to slightly change the code that replaces characters :
let s = "Hello playground"
let factor = 0.25
let factor2 = 0.45
var n = s.filter({ $0 != " " }).count // # of non-space characters
let nonSpaces = n
var m = lrint(factor * Double(n)) // # of characters to display
var indices = Array(s.indices)
var t = ""
for i in s.indices {
let c = s[i]
if c == " " {
// Preserve space
t.append(" ")
indices.removeAll(where: { $0 == i })
} else if Int.random(in: 0..<n) < m {
// Keep
m -= 1
n -= 1
t.append(c)
indices.removeAll(where: { $0 == i })
} else {
// Replace
n -= 1
t.append("_")
}
}
print(t) //For example: _e___ ______ou_d
Revealing Characters
In order to do that, we should calculate the number of characters that we want to reveal:
m = lrint((factor2 - factor) * Double(nonSpaces))
To pick three indices to reveal randomly, we shuffle indices and then replace the m first indices :
indices.shuffle()
var u = t
for i in 0..<m {
let index = indices[i]
u.replaceSubrange(index..<u.index(after: index), with: String(s[index]))
}
indices.removeSubrange(0..<m)
print(u) //For example: _e__o _l__g_ou_d
I wrote StringRevealer struct, that handle all revealing logic for you:
/// Hide all unicode letter characters as `_` symbol.
struct StringRevealer {
/// We need mapping between index of string character and his position in state array.
/// This struct represent one such record
private struct Symbol: Hashable {
let index: String.Index
let position: Int
}
private let originalString: String
private var currentState: [Character]
private let charactersCount: Int
private var revealed: Int
var revealedPercent: Double {
return Double(revealed) / Double(charactersCount)
}
private var unrevealedSymbols: Set<Symbol>
init(_ text: String) {
originalString = text
var state: [Character] = []
var symbols: [Symbol] = []
var count = 0
var index = originalString.startIndex
var i = 0
while index != originalString.endIndex {
let char = originalString[index]
if CharacterSet.letters.contains(char.unicodeScalars.first!) {
state.append("_")
symbols.append(Symbol(index: index, position: i))
count += 1
} else {
state.append(char)
}
index = originalString.index(after: index)
i += 1
}
currentState = state
charactersCount = count
revealed = 0
unrevealedSymbols = Set(symbols)
}
/// Current state of text. O(n) conplexity
func text() -> String {
return currentState.reduce(into: "") { $0.append($1) }
}
/// Reveal one random symbol in string
mutating func reveal() {
guard let symbol = unrevealedSymbols.randomElement() else { return }
unrevealedSymbols.remove(symbol)
currentState[symbol.position] = originalString[symbol.index]
revealed += 1
}
/// Reveal random symbols on string until `revealedPercent` > `percent`
mutating func reveal(until percent: Double) {
guard percent <= 1 else { return }
while revealedPercent < percent {
reveal()
}
}
}
var revealer = StringRevealer("Hello товарищ! 👋")
print(revealer.text())
print(revealer.revealedPercent)
for percent in [0.25, 0.45, 0.8] {
revealer.reveal(until: percent)
print(revealer.text())
print(revealer.revealedPercent)
}
It use CharacterSet.letters inside, so most of languages should be supported, emoji ignored and not-alphabetic characters as well.

Strange return value after generating random string (Swift 3)

I've created a method which generates and returns a random string of both letters and numbers, but for some reason I only get a string with numbers and the length of the string doesn't come close to what I asked it to be. A few examples of strings that have been returned: "478388299949939566" (inserted 18 as the length), "3772919388584334" (inserted 9 as the length), "2293010089409293945" (inserted 6 as the length). Anyone can see what I'm missing here?
func generateRandomStringWithLength(length:Int) -> String {
let randomString:NSMutableString = NSMutableString(capacity: length)
let letters:NSMutableString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
for index in 0...length {
let randomIndex:Int = Int(arc4random_uniform(UInt32(62)))
randomString.append("\(letters.character(at: randomIndex))")
}
return String(randomString)
}
Your problem is here:
letters.character(at: randomIndex)
it's function returns the character at a given UTF-16 code unit index, not not just a character
Here is my version, I guess its more swiftly.
func generateRandomStringWithLength(length: Int) -> String {
var randomString = ""
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
for _ in 1...length {
let randomIndex = Int(arc4random_uniform(UInt32(letters.characters.count)))
let a = letters.index(letters.startIndex, offsetBy: randomIndex)
randomString += String(letters[a])
}
return randomString
}
generateRandomStringWithLength(length: 5)
Use this:
func generateRandomStringWithLength(length:Int) -> String {
let randomString:NSMutableString = NSMutableString(capacity: length)
let letters:NSMutableString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
var i: Int = 0
while i < length {
let randomIndex:Int = Int(arc4random_uniform(UInt32(letters.length)))
randomString.appendString("\(Character( UnicodeScalar( letters.characterAtIndex(randomIndex))))")
i += 1
}
return String(randomString)
}
Calling generateRandomStringWithLength method:
print(generateRandomStringWithLength(5))
print(generateRandomStringWithLength(10))
print(generateRandomStringWithLength(20))
print(generateRandomStringWithLength(7))
print(generateRandomStringWithLength(14))
Sample Output:
GIrqb
nWmieQRVdk
r0It9V1xkGFRa2HVwtCw
RLIRuVI
nXnFGV2LQ3CjbD

Replace numeric of different length in a string with different text length

Problem: Numeric is of different length could be 1, 200, 1000, 39 99995 etc. And need to replace with a text eg. "Apple" which is of different lenth comapring to the numeric values.
let str: String = "hello i have 1313 object of 10 string class with 1 object, similar to 9999 errors"
Expected Result = "hello i have Apple object of Apple string class with Apple object, similar to Apple errors"
I have tried with below code:
var originalString: String = "hello i have 1313 object of 10 string class with 1 object, similar to 9999 errors"
let strippedString: NSMutableString = NSMutableString(capacity: originalString.characters.count)
var numArray: [String] = []
var locArray: [NSNumber] = []
var scanner: NSScanner = NSScanner(string:originalString)
let numbers: NSCharacterSet = NSCharacterSet(charactersInString: "0123456789")
while scanner.atEnd == false {
var buffer: NSString?
if scanner.scanCharactersFromSet(numbers, intoString: &buffer) {
strippedString.appendString(buffer! as String)
numArray.append(buffer! as String)
locArray.append(scanner.scanLocation)
}
else {
scanner.scanLocation = (scanner.scanLocation + 1)
}
}
for (index, _) in numArray.enumerate() {
var loc : Int = Int(locArray[index] ) - (String(numArray[index]).characters.count)
let len = String(numArray[index]).characters.count
let dupStr = "Apple"
if(index != 0 && len != dupStr.characters.count)
{
loc = loc + (dupStr.characters.count - len) + 1
}
originalString.replaceRange(originalString.startIndex.advancedBy(loc)..<originalString.startIndex.advancedBy(loc + len), with: dupStr)
}
print(originalString)
Swift 2
NSScanner is great, but if you want a simpler solution for this task, you could use componentsSeparatedByString, map, Int() and joinWithSeparator, like this:
let originalString = "hello i have 1313 object of 10 string class with 1 object, similar to 9999 errors"
let tokens = originalString.componentsSeparatedByString(" ")
let newTokens = tokens.map { (token) -> String in
if let _ = Int(token) {
return "Apple"
}
return token
}
let result = newTokens.joinWithSeparator(" ")
print(result)
Prints:
hello i have Apple object of Apple string class with Apple object, similar to Apple errors
There's also a short version for the mapping:
let newTokens = tokens.map { Int($0) != nil ? "Apple" : $0 }
Swift 3
componentsSeparatedByString(_:) is now components(separatedBy:), and joinWithSeparator(_:) is now joined(separator:).
let tokens = originalString.components(separatedBy: " ")
let newTokens = tokens.map { Int($0) != nil ? "Apple" : $0 }
let result = newTokens.joined(separator: " ")
var str: String = "hello i have 1313 object of 10 string class with 1 object, similar to 9999 errors"
var comp: [AnyObject] = [AnyObject](array: str.componentsSeparatedByString(" "))
for var i = 0; i < comp.count; i++ {
var numberRegex: NSRegularExpression = NSRegularExpression.regularExpressionWithPattern("^[0-9]", options: NSRegularExpressionCaseInsensitive, error: nil)
var regexMatch: Int = numberRegex.numberOfMatchesInString(comp[i], options: 0, range: NSMakeRange(0, (String(comp[i])).length))
if regexMatch != 0 {
comp[i] = "Apple"
}
}
var result: String = comp.componentsJoinedByString(" ")
print(result)

Swift - How to remove a decimal from a float if the decimal is equal to 0?

I'm displaying a distance with one decimal, and I would like to remove this decimal in case it is equal to 0 (ex: 1200.0Km), how could I do that in swift?
I'm displaying this number like this:
let distanceFloat: Float = (currentUser.distance! as NSString).floatValue
distanceLabel.text = String(format: "%.1f", distanceFloat) + "Km"
Swift 3/4:
var distanceFloat1: Float = 5.0
var distanceFloat2: Float = 5.540
var distanceFloat3: Float = 5.03
extension Float {
var clean: String {
return self.truncatingRemainder(dividingBy: 1) == 0 ? String(format: "%.0f", self) : String(self)
}
}
print("Value \(distanceFloat1.clean)") // 5
print("Value \(distanceFloat2.clean)") // 5.54
print("Value \(distanceFloat3.clean)") // 5.03
Swift 2 (Original answer)
let distanceFloat: Float = (currentUser.distance! as NSString).floatValue
distanceLabel.text = String(format: distanceFloat == floor(distanceFloat) ? “%.0f" : "%.1f", distanceFloat) + "Km"
Or as an extension:
extension Float {
var clean: String {
return self % 1 == 0 ? String(format: "%.0f", self) : String(self)
}
}
Use NSNumberFormatter:
let formatter = NumberFormatter()
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = 2
// Avoid not getting a zero on numbers lower than 1
// Eg: .5, .67, etc...
formatter.numberStyle = .decimal
let nums = [3.0, 5.1, 7.21, 9.311, 600.0, 0.5677, 0.6988]
for num in nums {
print(formatter.string(from: num as NSNumber) ?? "n/a")
}
Returns:
3
5.1
7.21
9.31
600
0.57
0.7
extension is the powerful way to do it.
Extension:
Code for Swift 2 (not Swift 3 or newer):
extension Float {
var cleanValue: String {
return self % 1 == 0 ? String(format: "%.0f", self) : String(self)
}
}
Usage:
var sampleValue: Float = 3.234
print(sampleValue.cleanValue)
3.234
sampleValue = 3.0
print(sampleValue.cleanValue)
3
sampleValue = 3
print(sampleValue.cleanValue)
3
Sample Playground file is here.
Update of accepted answer for swift 3:
extension Float {
var cleanValue: String {
return self.truncatingRemainder(dividingBy: 1) == 0 ? String(format: "%.0f", self) : String(self)
}
}
usage would just be:
let someValue: Float = 3.0
print(someValue.cleanValue) //prints 3
To format it to String, follow this pattern
let aFloat: Float = 1.123
let aString: String = String(format: "%.0f", aFloat) // "1"
let aString: String = String(format: "%.1f", aFloat) // "1.1"
let aString: String = String(format: "%.2f", aFloat) // "1.12"
let aString: String = String(format: "%.3f", aFloat) // "1.123"
To cast it to Int, follow this pattern
let aInt: Int = Int(aFloat) // "1"
When you use String(format: initializer, Swift will automatically round the final digit as needed based on the following number.
You can use an extension as already mentioned, this solution is a little shorter though:
extension Float {
var shortValue: String {
return String(format: "%g", self)
}
}
Example usage:
var sample: Float = 3.234
print(sample.shortValue)
Swift 5
for Double it's same as #Frankie's answer for float
var dec: Double = 1.0
dec.clean // 1
for the extension
extension Double {
var clean: String {
return self.truncatingRemainder(dividingBy: 1) == 0 ? String(format: "%.0f", self) : String(self)
}
}
Swift 5.5 makes it easy
Just use the new formatted() api with a default FloatingPointFormatStyle:
let values: [Double] = [1.0, 4.5, 100.0, 7]
for value in values {
print(value.formatted(FloatingPointFormatStyle()))
}
// prints "1, 4.5, 100, 7"
In Swift 4 try this.
extension CGFloat{
var cleanValue: String{
//return String(format: 1 == floor(self) ? "%.0f" : "%.2f", self)
return self.truncatingRemainder(dividingBy: 1) == 0 ? String(format: "%.0f", self) : String(format: "%.2f", self)//
}
}
//How to use - if you enter more then two-character after (.)point, it's automatically cropping the last character and only display two characters after the point.
let strValue = "32.12"
print(\(CGFloat(strValue).cleanValue)
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)
var offset = -maximumFractionDigits - 1
for i in stride(from: 0, to: -maximumFractionDigits, by: -1) {
if s[s.index(s.endIndex, offsetBy: i - 1)] != "0" {
offset = i
break
}
}
return String(s[..<s.index(s.endIndex, offsetBy: offset)])
}
}
(works also with extension Float, but not the macOS-only type Float80)
Usage: myNumericValue.string(maximumFractionDigits: 2) or myNumericValue.string()
Output for maximumFractionDigits: 2:
1.0 → "1"
0.12 → "0.12"
0.012 → "0.01"
0.0012 → "0"
0.00012 → "0"
Simple :
Int(floor(myFloatValue))
NSNumberFormatter is your friend
let distanceFloat: Float = (currentUser.distance! as NSString).floatValue
let numberFormatter = NSNumberFormatter()
numberFormatter.positiveFormat = "###0.##"
let distance = numberFormatter.stringFromNumber(NSNumber(float: distanceFloat))!
distanceLabel.text = distance + " Km"
Here's the full code.
let numberA: Float = 123.456
let numberB: Float = 789.000
func displayNumber(number: Float) {
if number - Float(Int(number)) == 0 {
println("\(Int(number))")
} else {
println("\(number)")
}
}
displayNumber(numberA) // console output: 123.456
displayNumber(numberB) // console output: 789
Here's the most important line in-depth.
func displayNumber(number: Float) {
Strips the float's decimal digits with Int(number).
Returns the stripped number back to float to do an operation with Float(Int(number)).
Gets the decimal-digit value with number - Float(Int(number))
Checks the decimal-digit value is empty with if number - Float(Int(number)) == 0
The contents within the if and else statements doesn't need explaining.
This might be helpful too.
extension Float {
func cleanValue() -> String {
let intValue = Int(self)
if self == 0 {return "0"}
if self / Float (intValue) == 1 { return "\(intValue)" }
return "\(self)"
}
}
Usage:
let number:Float = 45.23230000
number.cleanValue()
Maybe stringByReplacingOccurrencesOfString could help you :)
let aFloat: Float = 1.000
let aString: String = String(format: "%.1f", aFloat) // "1.0"
let wantedString: String = aString.stringByReplacingOccurrencesOfString(".0", withString: "") // "1"

Resources