Saintz
Saintz

Reputation: 69

How to trim a String using Swift 3

My code snippet is:

unwanted = " £€₹jetztabfromnow"

let favouritesPriceLabel = priceDropsCollectionView.cells.element(boundBy: UInt(index)).staticTexts[IPCUIAHighlightsPriceDropsCollectionViewCellPriceLabel].label
let favouritesPriceLabelTrimmed = favouritesPriceLabel.components(separatedBy: "jetzt").flatMap { String($0.trimmingCharacters(in: .whitespaces)) }.last

favouritesHighlightsDictionary[favouritesTitleLabel] = favouritesPriceLabelTrimmed

My problem is, this didn't work:

let favouritesPriceLabelTrimmed = favouritesPriceLabel.components(separatedBy: unwanted).flatMap { String($0.trimmingCharacters(in: .whitespaces)) }.last

I have a price like "from 3,95 €" - I want to cut all currencies "£€₹" and words like "from" or "ab"

Do you have a solution for me, what I can use here?

Upvotes: 2

Views: 1478

Answers (5)

Subramanian P
Subramanian P

Reputation: 4375

You can filter by special character by removing alphanumerics.

extension String {

    func removeCharacters(from forbiddenChars: CharacterSet) -> String {
        let passed = self.unicodeScalars.filter { !forbiddenChars.contains($0) }
        return String(String.UnicodeScalarView(passed))
    }
}

let str = "£€₹jetztabfromnow12"

let t1 = str.removeCharacters(from: CharacterSet.alphanumerics)
print(t1) // will print: £€₹

let t2 = str.removeCharacters(from: CharacterSet.decimalDigits.inverted)
print(t2) // will print: 12

Updated 1:

var str = "£3,95SS"

str = str.replacingOccurrences(of: ",", with: "")

let digit = str.removeCharacters(from: CharacterSet.decimalDigits.inverted)
print(digit) // will print: 395

let currency = str.removeCharacters(from: CharacterSet.alphanumerics)
print(currency) // will print: £

let amount = currency + digit
print(amount) // will print: £3,95

Update 2:

let string = "£3,95SS"

let pattern = "-?\\d+(,\\d+)*?\\.?\\d+?"
do {
    let regex = try NSRegularExpression(pattern: pattern, options: [])
    if let match = regex.firstMatch(in: string, range: NSRange(location: 0, length: string.utf16.count)) {
        let range = match.range
        let start = string.index(string.startIndex, offsetBy: range.location)
        let end = string.index(start, offsetBy: range.length)
        let digit = string.substring(with: start..<end)
        print(digit) //3,95

        let symbol = string.removeCharacters(from: CharacterSet.symbols.inverted)
        print(symbol) // £

        print(symbol + digit) //£3,95

    } else {
        print("Not found")
    }
} catch {
    print("Regex Error:", error)
}

Upvotes: 0

Tom Harrington
Tom Harrington

Reputation: 70956

Rather than mess around with trying to replace or remove the right characters or using regular expressions, I'd go with Foundation's built-in linguistic tagging support. It will do a lexical analysis of the string and return tokens of various types. Use it on this kind of string and it should reliably find any numbers in the string.

Something like:

var str = "from 3,95 €"

let range = Range(uncheckedBounds: (lower: str.startIndex, upper: str.endIndex))

var tokenRanges = [Range<String.Index>]()

let scheme = NSLinguisticTagSchemeLexicalClass
let option = NSLinguisticTagger.Options()
let tags = str.linguisticTags(in: range, scheme: scheme, options: option, orthography: nil, tokenRanges: &tokenRanges)
let tokens = tokenRanges.map { str.substring(with:$0) }

if let numberTagIndex = tags.index(where: { $0 == "Number" }) {
    let number = tokens[numberTagIndex]
    print("Found number: \(number)")
}

In this example the code prints "3,95". If you change str to "from £28.50", it prints "28.50".

Upvotes: 1

Abizern
Abizern

Reputation: 150615

I asked if you had a fixed locale for this string, because then you can use the locale to determine what the decimal separator is: For example, try this in a storyboard.

let string = "some initial text 3,95 €" // define the string to scan

// Add a convenience extension to Scanner so you don't have to deal with pointers directly.
extension Scanner {
    func scanDouble() -> Double? {
        var value = Double(0)
        guard scanDouble(&value) else { return nil }
        return value
    }

    // Convenience method to advance the location of the scanner up to the first digit. Returning the scanner itself or nil, which allows for optional chaining
    func scanUpToNumber() -> Scanner? {
        var value: NSString?
        guard scanUpToCharacters(from: CharacterSet.decimalDigits, into: &value) else { return nil }
        return self
    }
}

let scanner = Scanner(string: string)
scanner.locale = Locale(identifier: "fr_FR")
let double = scanner.scanUpToNumber()?.scanDouble() // -> double = 3.95 (note the type is Double?)

Scanners are a lot easier to use than NSRegularExpressions in these cases.

Upvotes: 0

vadian
vadian

Reputation: 285072

If you just want to extract the numeric value use regular expression, it considers comma or dot decimal separators.

let string = "from 3,95 €"

let pattern = "\\d+[.,]\\d+"
do {
    let regex = try NSRegularExpression(pattern: pattern, options: [])
    if let match = regex.firstMatch(in: string, range: NSRange(location: 0, length: string.utf16.count)) {
        let range = match.range
        let start = string.index(string.startIndex, offsetBy: range.location)
        let end = string.index(start, offsetBy: range.length)
        print(string.substring(with: start..<end)) // 3,95
    } else {
        print("Not found")
    }
} catch {
    print("Regex Error:", error)
}

Upvotes: 0

Joshua Kaden
Joshua Kaden

Reputation: 1230

One way is to place the unwanted strings into an array, and use String's replacingOccurrences(of:with:) method.

let stringToScan = "£28.50"
let toBeRemoved = ["£", "€", "₹", "ab", "from"]
var result = stringToScan
toBeRemoved.forEach { result = result.replacingOccurrences(of: $0, with: "") }
print(result)

...yields "28.50".

Upvotes: 0

Related Questions