How to parse string to NSTimeInterval - ios

How to parse string value like 12:02:21.3213 to NSTimeInterval? NSDateComponentsFormatter, available since iOS8, supports only formatting, not parsing.

Here is how you can do it in Swift,
It works for values like
2:12:12,
02:01:23.123213
Swift 5 (by #Youstanzr):
extension String {
func convertToTimeInterval() -> TimeInterval {
guard self != "" else {
return 0
}
var interval:Double = 0
let parts = self.components(separatedBy: ":")
for (index, part) in parts.reversed().enumerated() {
interval += (Double(part) ?? 0) * pow(Double(60), Double(index))
}
return interval
}
}
Swift 3 (by #Torre Lasley)
func parseDuration(_ timeString:String) -> TimeInterval {
guard !timeString.isEmpty else {
return 0
}
var interval:Double = 0
let parts = timeString.components(separatedBy: ":")
for (index, part) in parts.reversed().enumerated() {
interval += (Double(part) ?? 0) * pow(Double(60), Double(index))
}
return interval
}
Swift 2
func parseDuration(timeString:String) -> NSTimeInterval {
guard !timeString.isEmpty else {
return 0
}
var interval:Double = 0
let parts = timeString.componentsSeparatedByString(":")
for (index, part) in parts.reverse().enumerate() {
interval += (Double(part) ?? 0) * pow(Double(60), Double(index))
}
return interval
}

The solution provided by Bartosz Hernas worked for me, thank you!
For convenience, here it is for Swift 3:
func parseDuration(_ timeString:String) -> TimeInterval {
guard !timeString.isEmpty else {
return 0
}
var interval:Double = 0
let parts = timeString.components(separatedBy: ":")
for (index, part) in parts.reversed().enumerated() {
interval += (Double(part) ?? 0) * pow(Double(60), Double(index))
}
return interval
}

Here is the Swift 5 version that I've made of #Bartosz answer
extension String {
func convertToTimeInterval() -> TimeInterval {
guard self != "" else {
return 0
}
var interval:Double = 0
let parts = self.components(separatedBy: ":")
for (index, part) in parts.reversed().enumerated() {
interval += (Double(part) ?? 0) * pow(Double(60), Double(index))
}
return interval
}
}

Related

Jaro Winkler distance in Objective-C or Swift

I need to do fuzzy comparison of a large number of strings and am looking at Jaro-Winkler which respects differences in the order of letters. Is anyone aware of a way to do this in Objective-C or Swift either using Jaro-Winkler or some method native to IOS?
Thanks for any recommendations or suggestions.
I took an inspiration in Apache Commons and rewritten it to Swift:
extension String {
static func jaroWinglerDistance(_ first: String, _ second: String) -> Double {
let longer = Array(first.count > second.count ? first : second)
let shorter = Array(first.count > second.count ? second : first)
let (numMatches, numTranspositions) = jaroWinklerData(longer: longer, shorter: shorter)
if numMatches == 0 {
return 0
}
let defaultScalingFactor = 0.1;
let percentageRoundValue = 100.0;
let jaro = [
numMatches / Double(first.count),
numMatches / Double(second.count),
(numMatches - numTranspositions) / numMatches
].reduce(0, +) / 3
let jaroWinkler: Double
if jaro < 0.7 {
jaroWinkler = jaro
} else {
let commonPrefixLength = Double(commonPrefix(first, second).count)
jaroWinkler = jaro + Swift.min(defaultScalingFactor, 1 / Double(longer.count)) * commonPrefixLength * (1 - jaro)
}
return round(jaroWinkler * percentageRoundValue) / percentageRoundValue
}
private static func commonPrefix(_ first: String, _ second: String) -> String{
return String(
zip(first, second)
.prefix { $0.0 == $0.1 }
.map { $0.0 }
)
}
private static func jaroWinklerData(
longer: Array<Character>,
shorter: Array<Character>
) -> (numMatches: Double, numTranspositions: Double) {
let window = Swift.max(longer.count / 2 - 1, 0)
var shorterMatchedChars: [Character] = []
var longerMatches = Array<Bool>(repeating: false, count: longer.count)
for (offset, shorterChar) in shorter.enumerated() {
let windowRange = Swift.max(offset - window, 0) ..< Swift.min(offset + window + 1, longer.count)
if let matchOffset = windowRange.first(where: { !longerMatches[$0] && shorterChar == longer[$0] }) {
shorterMatchedChars.append(shorterChar)
longerMatches[matchOffset] = true
}
}
let longerMatchedChars = longerMatches
.enumerated()
.filter { $0.element }
.map { longer[$0.offset] }
let numTranspositions: Int = zip(shorterMatchedChars, longerMatchedChars)
.lazy
.filter { $0.0 != $0.1 }
.count / 2
return (
numMatches: Double(shorterMatchedChars.count),
numTranspositions: Double(numTranspositions)
)
}
}
Tested by the examples found in the original code:
print(String.jaroWinglerDistance("", ""))
print(String.jaroWinglerDistance("", "a"))
print(String.jaroWinglerDistance("aaapppp", ""))
print(String.jaroWinglerDistance("frog", "fog"))
print(String.jaroWinglerDistance("fly", "ant"))
print(String.jaroWinglerDistance("elephant", "hippo"))
print(String.jaroWinglerDistance("hippo", "elephant"))
print(String.jaroWinglerDistance("hippo", "zzzzzzzz"))
print(String.jaroWinglerDistance("hello", "hallo"))
print(String.jaroWinglerDistance("ABC Corporation", "ABC Corp"))
print(String.jaroWinglerDistance("D N H Enterprises Inc", "D & H Enterprises, Inc."))
print(String.jaroWinglerDistance("My Gym Children's Fitness Center", "My Gym. Childrens Fitness"))
print(String.jaroWinglerDistance("PENNSYLVANIA", "PENNCISYLVNIA"))
I have also found another implementation of String similarity functions in github.

How to make a number not repeat itself more than 2 times?

everyone ! Lets say we have
let random = arc4random_uniform(6)
how do i make it not repeat the same number more then two times ? I tried doing it like this :
let previousNumber = Int()
let lastNumber = Int ()
let random = Int(arc4random_uniform(6))
if random == previousNumber {
lastNumber = previousNumber
} else {
previousNumber = random
}
if random == lastNumber {
random = Int(arc4random_uniform(6))
}
But it didn't work. I am new to swift and i didn't find a topic about this on the new swift 3 code. Thank you !
First of all lets build a class to save the recent history of the selected values
class History {
private let size: Int
private var values = [Int]()
init(size:Int) {
self.size = size
}
func add(value: Int) {
values.insert(value, at: 0)
if values.count > size {
values.removeLast()
}
}
var repeatedValueOnFullHistory: Int? {
guard Set(values).count <= 1 else { return nil }
return values.first
}
}
Next let build a Randomizer
class Randomizer {
private var allValues = [Int]()
private var history: History
init?(maxValue: Int) {
guard maxValue > 0 else { return nil }
self.allValues = Array(0...maxValue)
self.history = History(size: maxValue + 1)
}
var next: Int {
let excludedValue = history.repeatedValueOnFullHistory
let allowedValues = allValues.filter { excludedValue != $0 }
let randomIndex = Int(arc4random_uniform(UInt32(allowedValues.count)))
let nextValue = allowedValues[randomIndex]
history.add(value: nextValue)
return nextValue
}
}
And finally let test it
if let r = Randomizer(maxValue: 6) {
r.next // 6
r.next // 2
r.next // 1
r.next // 4
r.next // 6
r.next // 4
r.next // 1
}

How to Convert duration form youtube api in swift?

I'm new swift developer i don't know how to convert duration from youtube api to normal time format?
A simpler implementation considering return value in hh:mm:ss format only.
extension String {
func getYoutubeFormattedDuration() -> String {
let formattedDuration = self.stringByReplacingOccurrencesOfString("PT", withString: "").stringByReplacingOccurrencesOfString("H", withString: ":").stringByReplacingOccurrencesOfString("M", withString: ":").stringByReplacingOccurrencesOfString("S", withString: "")
let components = formattedDuration.componentsSeparatedByString(":")
var duration = ""
for component in components {
duration = duration.characters.count > 0 ? duration + ":" : duration
if component.characters.count < 2 {
duration += "0" + component
continue
}
duration += component
}
return duration
}
}
**Swift 3
func getYoutubeFormattedDuration() -> String {
let formattedDuration = self.replacingOccurrences(of: "PT", with: "").replacingOccurrences(of: "H", with:":").replacingOccurrences(of: "M", with: ":").replacingOccurrences(of: "S", with: "")
let components = formattedDuration.components(separatedBy: ":")
var duration = ""
for component in components {
duration = duration.characters.count > 0 ? duration + ":" : duration
if component.characters.count < 2 {
duration += "0" + component
continue
}
duration += component
}
return duration
}
Sample Results:
"PT3H2M31S".getYoutubeFormattedDuration() //returns "03:02:31"
"PT2M31S".getYoutubeFormattedDuration() //returns "02:31"
"PT31S".getYoutubeFormattedDuration() //returns "31"
Using String Extension:
extension String{
func formatDurationsFromYoutubeAPItoNormalTime (targetString : String) ->String{
var timeDuration : NSString!
let string: NSString = targetString
if string.rangeOfString("H").location == NSNotFound && string.rangeOfString("M").location == NSNotFound{
if string.rangeOfString("S").location == NSNotFound {
timeDuration = NSString(format: "00:00")
} else {
var secs: NSString = targetString
secs = secs.substringFromIndex(secs.rangeOfString("PT").location + "PT".characters.count)
secs = secs.substringToIndex(secs.rangeOfString("S").location)
timeDuration = NSString(format: "00:%02d", secs.integerValue)
}
}
else if string.rangeOfString("H").location == NSNotFound {
var mins: NSString = targetString
mins = mins.substringFromIndex(mins.rangeOfString("PT").location + "PT".characters.count)
mins = mins.substringToIndex(mins.rangeOfString("M").location)
if string.rangeOfString("S").location == NSNotFound {
timeDuration = NSString(format: "%02d:00", mins.integerValue)
} else {
var secs: NSString = targetString
secs = secs.substringFromIndex(secs.rangeOfString("M").location + "M".characters.count)
secs = secs.substringToIndex(secs.rangeOfString("S").location)
timeDuration = NSString(format: "%02d:%02d", mins.integerValue, secs.integerValue)
}
} else {
var hours: NSString = targetString
hours = hours.substringFromIndex(hours.rangeOfString("PT").location + "PT".characters.count)
hours = hours.substringToIndex(hours.rangeOfString("H").location)
if string.rangeOfString("M").location == NSNotFound && string.rangeOfString("S").location == NSNotFound {
timeDuration = NSString(format: "%02d:00:00", hours.integerValue)
} else if string.rangeOfString("M").location == NSNotFound {
var secs: NSString = targetString
secs = secs.substringFromIndex(secs.rangeOfString("H").location + "H".characters.count)
secs = secs.substringToIndex(secs.rangeOfString("S").location)
timeDuration = NSString(format: "%02d:00:%02d", hours.integerValue, secs.integerValue)
} else if string.rangeOfString("S").location == NSNotFound {
var mins: NSString = targetString
mins = mins.substringFromIndex(mins.rangeOfString("H").location + "H".characters.count)
mins = mins.substringToIndex(mins.rangeOfString("M").location)
timeDuration = NSString(format: "%02d:%02d:00", hours.integerValue, mins.integerValue)
} else {
var secs: NSString = targetString
secs = secs.substringFromIndex(secs.rangeOfString("M").location + "M".characters.count)
secs = secs.substringToIndex(secs.rangeOfString("S").location)
var mins: NSString = targetString
mins = mins.substringFromIndex(mins.rangeOfString("H").location + "H".characters.count)
mins = mins.substringToIndex(mins.rangeOfString("M").location)
timeDuration = NSString(format: "%02d:%02d:%02d", hours.integerValue, mins.integerValue, secs.integerValue)
}
}
return timeDuration as String
}
}
Usage:
override func viewWillAppear(animated: Bool) {
let youtubeVideoDurationString = "PT15M51S"
let resultString = youtubeVideoDurationString.formatDurationsFromYoutubeAPItoNormalTime(youtubeVideoDurationString)
print(resultString)
}
To Convert Hour:Minute:Second format :-
Using String Extension:
extension String{
func parseVideoDurationOfYoutubeAPI(videoDuration: String?) -> String {
var videoDurationString = videoDuration! as NSString
var hours: Int = 0
var minutes: Int = 0
var seconds: Int = 0
let timeRange = videoDurationString.rangeOfString("T")
videoDurationString = videoDurationString.substringFromIndex(timeRange.location)
while videoDurationString.length > 1 {
videoDurationString = videoDurationString.substringFromIndex(1)
let scanner = NSScanner(string: videoDurationString as String) as NSScanner
var part: NSString?
scanner.scanCharactersFromSet(NSCharacterSet.decimalDigitCharacterSet(), intoString: &part)
let partRange: NSRange = videoDurationString.rangeOfString(part! as String)
videoDurationString = videoDurationString.substringFromIndex(partRange.location + partRange.length)
let timeUnit: String = videoDurationString.substringToIndex(1)
if (timeUnit == "H") {
hours = Int(part as! String)!
}
else if (timeUnit == "M") {
minutes = Int(part as! String)!
}
else if (timeUnit == "S") {
seconds = Int(part! as String)!
}
else{
}
}
return String(format: "%02d:%02d:%02d", hours, minutes, seconds)
}
}
Usage:
override func viewWillAppear(animated: Bool) {
let youtubeVideoDurationString = "PT15M51S"
let result_Hour_Minute_Second = youtubeVideoDurationString.parseVideoDurationOfYoutubeAPI("PT15M51S") as String
print("result_Hour_Minute_Second: \(result_Hour_Minute_Second)")
}
In Swift 5
extension String {
func getYoutubeFormattedDuration() -> String {
let formattedDuration = self.replacingOccurrences(of: "PT", with: "").replacingOccurrences(of: "H", with:":").replacingOccurrences(of: "M", with: ":").replacingOccurrences(of: "S", with: "")
let components = formattedDuration.components(separatedBy: ":")
var duration = ""
for component in components {
duration = duration.count > 0 ? duration + ":" : duration
if component.count < 2 {
duration += "0" + component
continue
}
duration += component
}
return duration
}
}
Usage:
let youtubeVideoDurationString = "PT2M24S"
youtubeVideoDurationString.getYoutubeFormattedDuration() //returns 02:24

Swift 2.0 Format 1000's into a friendly K's

I'm trying to write a function to present thousands and millions into K's and M's
For instance:
1000 = 1k
1100 = 1.1k
15000 = 15k
115000 = 115k
1000000 = 1m
Here is where I got so far:
func formatPoints(num: Int) -> String {
let newNum = String(num / 1000)
var newNumString = "\(num)"
if num > 1000 && num < 1000000 {
newNumString = "\(newNum)k"
} else if num > 1000000 {
newNumString = "\(newNum)m"
}
return newNumString
}
formatPoints(51100) // THIS RETURNS 51K instead of 51.1K
How do I get this function to work, what am I missing?
extension Int {
var roundedWithAbbreviations: String {
let number = Double(self)
let thousand = number / 1000
let million = number / 1000000
if million >= 1.0 {
return "\(round(million*10)/10)M"
}
else if thousand >= 1.0 {
return "\(round(thousand*10)/10)K"
}
else {
return "\(self)"
}
}
}
print(11.roundedWithAbbreviations) // "11"
print(11111.roundedWithAbbreviations) // "11.1K"
print(11111111.roundedWithAbbreviations) // "11.1 M"
func formatPoints(num: Double) ->String{
let thousandNum = num/1000
let millionNum = num/1000000
if num >= 1000 && num < 1000000{
if(floor(thousandNum) == thousandNum){
return("\(Int(thousandNum))k")
}
return("\(thousandNum.roundToPlaces(1))k")
}
if num > 1000000{
if(floor(millionNum) == millionNum){
return("\(Int(thousandNum))k")
}
return ("\(millionNum.roundToPlaces(1))M")
}
else{
if(floor(num) == num){
return ("\(Int(num))")
}
return ("\(num)")
}
}
extension Double {
/// Rounds the double to decimal places value
func roundToPlaces(places:Int) -> Double {
let divisor = pow(10.0, Double(places))
return round(self * divisor) / divisor
}
}
The updated code should now not return a .0 if the number is whole. Should now output 1k instead of 1.0k for example. I just checked essentially if double and its floor were the same.
I found the double extension in this question:
Rounding a double value to x number of decimal places in swift
The extension below does the following-
Will display number 10456 as 10.5k and 10006 as 10k (will not show the .0 decimals).
Will do the exact above for millions and format it i.e. 10.5M and 10M
Will format thousands upto 9999 in currency format i.e. with a comma like 9,999
extension Double {
var kmFormatted: String {
if self >= 10000, self <= 999999 {
return String(format: "%.1fK", locale: Locale.current,self/1000).replacingOccurrences(of: ".0", with: "")
}
if self > 999999 {
return String(format: "%.1fM", locale: Locale.current,self/1000000).replacingOccurrences(of: ".0", with: "")
}
return String(format: "%.0f", locale: Locale.current,self)
}
}
Usage:
let num: Double = 1000001.00 //this should be a Double since the extension is on Double
let millionStr = num.kmFormatted
print(millionStr)
Prints 1M
And here it is in action-
To add to the answers, here's a Swift 4.X version of this using a loop to easily add/remove units if necessary:
extension Double {
var shortStringRepresentation: String {
if self.isNaN {
return "NaN"
}
if self.isInfinite {
return "\(self < 0.0 ? "-" : "+")Infinity"
}
let units = ["", "k", "M"]
var interval = self
var i = 0
while i < units.count - 1 {
if abs(interval) < 1000.0 {
break
}
i += 1
interval /= 1000.0
}
// + 2 to have one digit after the comma, + 1 to not have any.
// Remove the * and the number of digits argument to display all the digits after the comma.
return "\(String(format: "%0.*g", Int(log10(abs(interval))) + 2, interval))\(units[i])"
}
}
Examples:
$ [1.5, 15, 1000, 1470, 1000000, 1530000, 1791200000].map { $0.shortStringRepresentation }
[String] = 7 values {
[0] = "1.5"
[1] = "15"
[2] = "1k"
[3] = "1.5k"
[4] = "1M"
[5] = "1.5M"
[6] = "1791.2M"
}
Some Change in Answer(For Int and correct for million):
func formatPoints(num: Int) ->String{
let thousandNum = num/1000
let millionNum = num/1000000
if num >= 1000 && num < 1000000{
if(thousandNum == thousandNum){
return("\(thousandNum)k")
}
return("\(thousandNum)k")
}
if num > 1000000{
if(millionNum == millionNum){
return("\(millionNum)M")
}
return ("\(millionNum)M")
}
else{
if(num == num){
return ("\(num)")
}
return ("\(num)")
}
}
For swift 4.0. its work completely fine and answer based on #user3483203
Function for convert Double value to String
func formatPoints(num: Double) ->String{
var thousandNum = num/1000
var millionNum = num/1000000
if num >= 1000 && num < 1000000{
if(floor(thousandNum) == thousandNum){
return("\(Int(thousandNum))k")
}
return("\(thousandNum.roundToPlaces(places: 1))k")
}
if num > 1000000{
if(floor(millionNum) == millionNum){
return("\(Int(thousandNum))k")
}
return ("\(millionNum.roundToPlaces(places: 1))M")
}
else{
if(floor(num) == num){
return ("\(Int(num))")
}
return ("\(num)")
}
}
Make one Double extension
extension Double {
/// Rounds the double to decimal places value
mutating func roundToPlaces(places:Int) -> Double {
let divisor = pow(10.0, Double(places))
return Darwin.round(self * divisor) / divisor
}
}
Usage of above function
UILABEL.text = formatPoints(num: Double(310940)!)
Output :
Here is my approach to it.
extension Int {
func shorted() -> String {
if self >= 1000 && self < 10000 {
return String(format: "%.1fK", Double(self/100)/10).replacingOccurrences(of: ".0", with: "")
}
if self >= 10000 && self < 1000000 {
return "\(self/1000)k"
}
if self >= 1000000 && self < 10000000 {
return String(format: "%.1fM", Double(self/100000)/10).replacingOccurrences(of: ".0", with: "")
}
if self >= 10000000 {
return "\(self/1000000)M"
}
return String(self)
}
Below are some examples:
print(913.shorted())
print(1001.shorted())
print(1699.shorted())
print(8900.shorted())
print(10500.shorted())
print(17500.shorted())
print(863500.shorted())
print(1200000.shorted())
print(3010000.shorted())
print(11800000.shorted())
913
1K
1.6K
8.9K
10k
17k
863k
1.2M
3M
11M
Solution above (from #qlear) in Swift 3:
func formatPoints(num: Double) -> String {
var thousandNum = num / 1_000
var millionNum = num / 1_000_000
if num >= 1_000 && num < 1_000_000 {
if floor(thousandNum) == thousandNum {
return("\(Int(thousandNum))k")
}
return("\(thousandNum.roundToPlaces(1))k")
}
if num > 1_000_000 {
if floor(millionNum) == millionNum {
return "\(Int(thousandNum))k"
}
return "\(millionNum.roundToPlaces(1))M"
}
else{
if floor(num) == num {
return "\(Int(num))"
}
return "\(num)"
}
}
extension Double {
// Rounds the double to decimal places value
mutating func roundToPlaces(_ places : Int) -> Double {
let divisor = pow(10.0, Double(places))
return (self.rounded() * divisor) / divisor
}
}
I have converted #AnBisw's answer to use switch (build time friendly):
extension Double {
var kmFormatted: String {
switch self {
case ..<1_000:
return String(format: "%.0f", locale: Locale.current, self)
case 1_000 ..< 999_999:
return String(format: "%.1fK", locale: Locale.current, self / 1_000).replacingOccurrences(of: ".0", with: "")
default:
return String(format: "%.1fM", locale: Locale.current, self / 1_000_000).replacingOccurrences(of: ".0", with: "")
}
}
}
Based on the solution from #qlear.
I've noticed that if the number was exactly 1000000, it would return 1000000 unformatted.
So I've added that into the function. I also included negative values.. since not everyone is always making a profit!
func formatPoints(num: Double) ->String{
let thousandNum = num/1000
let millionNum = num/1000000
if num > 0
{
if num >= 1000 && num < 1000000{
if(floor(thousandNum) == thousandNum){
return("\(Int(thousandNum))k")
}
return("\(round1(thousandNum, toNearest: 0.01))k")
}
if num > 1000000{
if(floor(millionNum) == millionNum){
return("\(Int(thousandNum))k")
}
return ("\(round1(millionNum, toNearest: 0.01))M")
}
else if num == 1000000
{
return ("\(round1(millionNum, toNearest: 0.01))M")
}
else{
if(floor(num) == num){
return ("\(Int(num))")
}
return ("\(round1(num, toNearest: 0.01))")
}
}
else
{
if num <= -1000 && num > -1000000{
if(floor(thousandNum) == thousandNum){
return("\(Int(thousandNum))k")
}
return("\(round1(thousandNum, toNearest: 0.01))k")
}
if num < -1000000{
if(floor(millionNum) == millionNum){
return("\(Int(thousandNum))k")
}
return ("\(round1(millionNum, toNearest: 0.01))M")
}
else if num == -1000000
{
return ("\(round1(millionNum, toNearest: 0.01))M")
}
else{
if(floor(num) == num){
return ("\(Int(num))")
}
return ("\(round1(num, toNearest: 0.01))")
}
}
}
And the number extension of course:
extension Double {
/// Rounds the double to decimal places value
func round1(_ value: Double, toNearest: Double) -> Double {
return Darwin.round(value / toNearest) * toNearest
}
}
This solution utilises ByteCountFormatter, but for any big number abbreviation of any number type. Why this formatter written by Apple for bytes only remains unknown.
extension Numeric {
var abbreviated: String {
let bytesString = ByteCountFormatter.string(fromByteCount: (self as! NSNumber).int64Value, countStyle: .decimal)
let numericString = bytesString
.replacingOccurrences(of: "bytes", with: "")
.replacingOccurrences(of: "B", with: "") // removes B (bytes) in 'KB'/'MB'/'GB'
.replacingOccurrences(of: "G", with: "B") // replace G (Giga) to just B (billions)
return numericString.replacingOccurrences(of: " ", with: "")
}
}
Minor improvement to #chrisz answer, Swift-4 Doble extension - This works fine in all cases.
extension Double {
// Formatting double value to k and M
// 1000 = 1k
// 1100 = 1.1k
// 15000 = 15k
// 115000 = 115k
// 1000000 = 1m
func formatPoints() -> String{
let thousandNum = self/1000
let millionNum = self/1000000
if self >= 1000 && self < 1000000{
if(floor(thousandNum) == thousandNum){
return ("\(Int(thousandNum))k").replacingOccurrences(of: ".0", with: "")
}
return("\(thousandNum.roundTo(places: 1))k").replacingOccurrences(of: ".0", with: "")
}
if self > 1000000{
if(floor(millionNum) == millionNum){
return("\(Int(thousandNum))k").replacingOccurrences(of: ".0", with: "")
}
return ("\(millionNum.roundTo(places: 1))M").replacingOccurrences(of: ".0", with: "")
}
else{
if(floor(self) == self){
return ("\(Int(self))")
}
return ("\(self)")
}
}
/// Returns rounded value for passed places
///
/// - parameter places: Pass number of digit for rounded value off after decimal
///
/// - returns: Returns rounded value with passed places
func roundTo(places:Int) -> Double {
let divisor = pow(10.0, Double(places))
return (self * divisor).rounded() / divisor
}
}
if you want in lacs :
extension Int {
func shorted() -> String {
if self >= 1000 && self < 10000 {
return String(format: "%.1fK", Double(self/100)/10).replacingOccurrences(of: ".0", with: "")
}
if self >= 10000 && self < 100000 {
return "\(self/1000)k"
}
if self >= 100000 && self < 1000000 {
return String(format: "%.1fL", Double(self/10000)/10).replacingOccurrences(of: ".0", with: "")
}
if self >= 1000000 && self < 10000000 {
return String(format: "%.1fM", Double(self/100000)/10).replacingOccurrences(of: ".0", with: "")
}
if self >= 10000000 {
return "\(self/1000000)M"
}
return String(self)
}
}
Since we all more or less disagree
func FormatFriendly(num: Double) ->String {
var thousandNum = num/1000
var millionNum = num/1000000
if num >= 1000 && num < 1000000{
if(floor(thousandNum) == thousandNum){
return("\(Int(thousandNum))K").replacingOccurrences(of: ".0", with: "")
}
return("\(thousandNum.roundToPlaces(places: 1))K").replacingOccurrences(of: ".0", with: "")
}
if num >= 1000000{
//if(floor(millionNum) == millionNum){
//return("\(Int(thousandNum))K").replacingOccurrences(of: ".0", with: "")
//}
return ("\(millionNum.roundToPlaces(places: 1))M").replacingOccurrences(of: ".0", with: "")
}else {
if(floor(num) == num){
return ("\(Int(num))")
}
return ("\(num)")
}
}
extension Double {
/// Rounds the double to decimal places value
mutating func roundToPlaces(places: Int) -> Double {
let divisor = pow(10.0, Double(places))
return Darwin.round(self * divisor) / divisor
}
}

NSExpression Calculator in Swift

I am trying to duplicate Need to write calculator in Objective-C in Swift but my code is not working.
import Foundation
var equation:NSString = "5*(2.56-1.79)-4.1"
var result = NSExpression(format: equation, argumentArray: nil)
println(result)
As already said in a comment, you have to call expressionValueWithObject()
on the expression:
let expr = NSExpression(format: equation)
if let result = expr.expressionValueWithObject(nil, context: nil) as? NSNumber {
let x = result.doubleValue
println(x)
} else {
println("failed")
}
Update for Swift 3:
let expr = NSExpression(format: equation)
if let result = expr.expressionValue(with: nil, context: nil) as? Double {
print(result) // -0.25
} else {
print("failed")
}
Details
Xcode 9.4.1, Swift 4.1
Xcode 10.2.1 (10E1001), Swift 5
Solution
import Foundation
extension String {
private func allNumsToDouble() -> String {
let symbolsCharSet = ".,"
let fullCharSet = "0123456789" + symbolsCharSet
var i = 0
var result = ""
var chars = Array(self)
while i < chars.count {
if fullCharSet.contains(chars[i]) {
var numString = String(chars[i])
i += 1
loop: while i < chars.count {
if fullCharSet.contains(chars[i]) {
numString += String(chars[i])
i += 1
} else {
break loop
}
}
if let num = Double(numString) {
result += "\(num)"
} else {
result += numString
}
} else {
result += String(chars[i])
i += 1
}
}
return result
}
func calculate() -> Double? {
let transformedString = allNumsToDouble()
let expr = NSExpression(format: transformedString)
return expr.expressionValue(with: nil, context: nil) as? Double
}
}
Usage
"3*(3-1)-5".calculate()
Full sample
func test(_ expressrion: String) {
if let num = expressrion.calculate() {
print("\(expressrion) = \(num)")
} else {
print("\(expressrion) = nil")
}
}
test("3*(3-1)-5")
test("5.2*(2-1.79)-5.1")
test("11/5")
Results

Resources