swiftnsstringnscharactersetnsformatter

Convert large mixed numerical and text numbers into numbers in Swift or Objective-C


NSFormatter's spellout method lets you convert a word into a number. NSString's stringByReplacingOccurrencesOfString lets you convert strings such as thousand into 1000. And you can pick integers out of strings using NSCharacterSet. However, I am struggling with how to convert a mixture of numbers and strings, for example, about 2.4 million or the comes to 5 hundred into a number. The problem is isolating the '2.4 million' from the 'about' or other text.

Applying spellout to "2.4 million" yields 2400000. However, applying it to "about 2.4 million" gives an error.

extension NSString {
    public var asNum: NSNumber {
       // let stringValue = String(value: self)
        let stringValue = self
        let formatter = NumberFormatter()
        formatter.isLenient = true
        formatter.numberStyle = .spellOut
        return formatter.number(from: stringValue as String) ?? -1
    }
}

How can I isolate just the terms that are part of a valid number?


Solution

  • If you used a regular expression you could extract just the string with the numeric expression. Assuming that the extra words are at the beginning (as you said in a comment) you can do the following:

    You can add just three lines to your code and it should work fine, something like this:

    extension NSString {
        public var asNum: NSNumber {
            let stringValue = self
            let range = stringValue.range(of: #"\d+(\.\d+)*\s+\w+"#,
                                            options: .regularExpression)
            guard range.location != NSNotFound else { return -1 }
            let numberExpression = stringValue.substring(with: range)
    
            let formatter = NumberFormatter()
            formatter.isLenient = true
            formatter.numberStyle = .spellOut
            return formatter.number(from: numberExpression) ?? -1
        }
    }
    

    If we follow @rmaaddy suggestion of returning an optional instead of the "magic number" -1 then the code looks like this:

    extension NSString { 
        public var asNum: NSNumber? {
            let stringValue = self
            let range = stringValue.range(of: #"\d+(\.\d+)*\s+\w+"#,
                                            options: .regularExpression)
            guard range.location != NSNotFound else { return nil }
            let numberExpression = stringValue.substring(with: range)
    
            let formatter = NumberFormatter()
            formatter.isLenient = true
            formatter.numberStyle = .spellOut
            return formatter.number(from: numberExpression) ?? nil
        }
    }