0

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?

user1904273
  • 4,562
  • 11
  • 45
  • 96
  • Not related to your question but don't return magic numbers such as `-1`. Make the return value optional and return `nil` for invalid numbers. – rmaddy Apr 04 '19 at 00:12
  • Are the extra words you wish to ignore always at the start of the text? You could try to parse the string as a number. If it fails, drop the first word and try again. Keep dropping the next leading word until it works or you run out of text. – rmaddy Apr 04 '19 at 00:14
  • yes they usually are at beginning. Will give it a try – user1904273 Apr 04 '19 at 00:16

1 Answers1

1

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:

  • Search for the numerical expression using a regular expression, in this case the expression is a number follow by one word. \d+(\.\d+)*\s+\w+
  • Check that the expression was found range.location != NSNotFound
  • Extract the match into a String

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
    }
}
Aaron Cyrman
  • 556
  • 3
  • 13