2

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?

2
  • Is there a fixed locale for this string? Commented Jun 15, 2017 at 14:20
  • None. It can change between more locales. Commented Jun 16, 2017 at 7:14

5 Answers 5

1

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".

Sign up to request clarification or add additional context in comments.

2 Comments

Hi, i test your version. But i have an question. I wanna put your version in a helper method. I have more than one price to check and to trim on distributed at several places. How can i make an hand over from the test to the helper and back again to get the results an work with it further. Is it possible to change it to an double?
I'm not sure I understand. It sounds like you would want to create a func that contains this code, with the string as an argument. Return the result of the linguistic analysis from the func. Strings can be converted into numeric types, so the function can return a number instead of a string.
0

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".

Comments

0

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) } 

Comments

0

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.

1 Comment

Sorry, i saw it today, that you asked. None, it can be changed between more locales, like (FR, IT, DE, UK, ES and IN (UK))
0

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) } 

18 Comments

Ok, if i print (t1) i will get the £€₹, but i want to get the 3,95
do you want to get only numbers? updated my answer to get the digits from string
Will that work when the number is 3,95? Does it remove the decimal point/comma ?
Ok, and how i can put the String where i get the price in it? See at my example upper there... favouritesPriceLabelTrimmed is the String with the full String like "from 3,95 €" Where should it be placed in let t2 = str.removeCharacters(from: CharacterSet.decimalDigits.inverted)
Worked, but do you have an idea to give it back as an Double and not as an String digit? If i do this : let favouritesPriceLabelTrimmed = favouritesPriceLabel.removeCharacters(from: CharacterSet.decimalDigits.inverted).toDouble(with: favouritesPriceLabel) I get on : favouritesHighlightsDictionary[favouritesTitleLabel] = favouritesPriceLabelTrimmed an bug : "Ambiguous reference to member"
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.