Trim String before specific character? - ios

I have a string (asderwt.qwertyu.zxcvbbnnhg) and I want to change it to .qwertyu.zxcvbbnnhg.
I have tried to do this using the following code:
Var str = "asderwt.qwertyu.zxcvbbnnhg"
if let dotRange = str.range(of: ".") {
str.removeSubrange(dotRange.lowerBound..<str.startIndex)
print("AAAAAAAAAA: \(str)")
}

Get the first index of the dot and get the substring after that index
let str = "asderwt.qwertyu.zxcvbbnnhg"
if let index = str.firstIndex(where: { $0 == "." }) {
print(str[index...])//.qwertyu.zxcvbbnnhg
}

You are on the right track, but here
str.removeSubrange(dotRange.lowerBound..<str.startIndex)
the range bounds are in the wrong order. It should be
var str = "asderwt.qwertyu.zxcvbbnnhg"
if let dotRange = str.range(of: ".") {
str.removeSubrange(str.startIndex..<dotRange.lowerBound) // <-- HERE
print("Result: \(str)") // Result: .qwertyu.zxcvbbnnhg
}
You can also use a “partial range”
if let dotRange = str.range(of: ".") {
str.removeSubrange(..<dotRange.lowerBound)
print("Result: \(str)") // Result: .qwertyu.zxcvbbnnhg
}
Or with firstIndex(of:) instead of range(of:):
if let dotIndex = str.firstIndex(of: ".") {
str.removeSubrange(..<dotIndex)
print("Result: \(str)") // Result: .qwertyu.zxcvbbnnhg
}

Related

How can I split a string with occurrence of "users/" from a URL?

I Am using this method, this is my URL "https://www.youtube.com/user/myYoutubeChennal". and i want to get "myYoutubeChennal" from this.
func splitVideoUrl(videoUrl:String) -> String {
//let token = videoUrl.split(separator: "user/")
let ttstr:String = " https://www.youtube.com/user/myYoutubeChennal"
if let range = ttstr.range(of: "user/") {
let firstPart = String(describing: ttstr.startIndex..<range.lowerBound)
print(firstPart) // print Hello
return firstPart
}
return ""
}
You can get the last component of URL using following code
let url = URL(string:"https://www.youtube.com/user/myYoutubeChennal")
let lastComponent = url?.lastPathComponent
Assuming the desired string is always the path component after user split the string by slashes, get the index of user and then the item index + 1
let string = "https://www.youtube.com/user/myYoutubeChennal"
let components = string.components(separatedBy: "/")
if let index = components.index(of: "user"), index < components.count - 1 {
let user = components[index + 1]
} else {
print("user not found")
}

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?
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".
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".
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)
}
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.
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)
}

Delete characters in range of string

I have the following string I would like to edit:
var someString = "I wan't this text {something I don't want}"
I would like to remove all the text contained in the two braces, no matter how long that text is. I have been using the follow code to remove a section of a String when I know the range:
extension String {
mutating func deleteCharactersInRange(range: NSRange) {
let mutableSelf = NSMutableString(string: self)
mutableSelf.deleteCharactersInRange(range)
self = mutableSelf
}
}
However, I do not know the range in my problem. Any ideas?
Working with strings and ranges can be quite challenging when mixing NSString and NSRange with Swift's String and Range.
Here is a pure Swift solution.
var someString = "I wan't this text {something I don't want}"
let rangeOpenCurl = someString.rangeOfString("{")
let rangeCloseCurl = someString.rangeOfString("}")
if let startLocation = rangeOpenCurl?.startIndex,
let endLocation = rangeCloseCurl?.endIndex {
someString.replaceRange(startLocation ..< endLocation, with: "")
}
With a RegEx pattern to match anything enclosed with curly brackets:
var sourceString: String = "I wan\'t this text {something I don't want}"
let destinationString = sourceString.stringByReplacingOccurrencesOfString("\\{(.*?)\\}", withString: "", options: .RegularExpressionSearch)
print(destinationString)
This will print "I wan't this text " without the double quotes.
extension String {
func getCurlyBraceRanges() -> [NSRange] {
var results = [NSRange]()
var leftCurlyBrace = -1
for index in 0..<self.characters.count {
let char = self[self.startIndex.advancedBy(index)]
if char == Character("{") {
leftCurlyBrace = index
} else if char == Character("}") {
if leftCurlyBrace != -1 {
results.append(NSRange(location: leftCurlyBrace, length: index - leftCurlyBrace + 1))
leftCurlyBrace = -1
}
}
}
return results
}
mutating func deleteCharactersInRange(range: NSRange) {
let mutableSelf = NSMutableString(string: self)
mutableSelf.deleteCharactersInRange(range)
self = String(mutableSelf)
}
mutating func deleteCharactersInRanges(ranges: [NSRange]) {
var tmpString = self
for i in (0..<ranges.count).reverse() {
tmpString.deleteCharactersInRange(ranges[i])
print(tmpString)
}
self = tmpString
}
}
var testString = "I wan't this text {something I don't want}"
testString.deleteCharactersInRanges(testString.getCurlyBraceRanges())
Output: "I wan't this text "

Ignore a letter in swift which starts with a Lower Case

Here's what I am trying to do :
let courseName = "Bachelor of Tourism Administration(B.T.A)".condensedWhitespace
let upperCaseCourseName = courseName.uppercaseString
let extrctCourseName = upperCaseCourseName.componentsSeparatedByString(" ").reduce("") { $0.0 + String($0.1.characters.first!) }
let upperCasecourseFirstCharcters = extrctCourseName
print(upperCasecourseFirstCharcters) // output : "BOTA" but i want "BTA"
as you see that my outPut of "Bachelor of Tourism Administration(B.T.A)" is BOTA but the desired output is BTA because word of is starting from a lowerCase and i want to ignore that word in my this method , how am gonna do that any idea ?
let courseName = "Bachelor of Tourism Administration(B.T.A)" //.condensedWhitespace
var newString = ""
let array : NSArray = courseName.componentsSeparatedByString(" ")
for chr in array {
let str = chr as! NSString
if str.lowercaseString != str{
if newString.characters.count > 0{
newString = newString.stringByAppendingString(" "+(str as String))
continue
}
newString = newString.stringByAppendingString((str as String))
}
}
let upperCaseCourseName = newString.uppercaseString
let extrctCourseName = upperCaseCourseName.componentsSeparatedByString(" ").reduce("") { $0.0 + String($0.1.characters.first!) }
let upperCasecourseFirstCharcters = extrctCourseName
print(upperCasecourseFirstCharcters)
//This will defiantly meet to your problem/. Let me know if it works for u or not
You can paste this into a playground:
extension String {
func array() -> [String] {
return self.componentsSeparatedByString(" ")
}
func abbreviate() -> String {
var output = ""
let array = self.array()
for word in array {
let index = word.startIndex.advancedBy(0)
let str = String(word[index])
if str.lowercaseString != str {
output += str
}
}
return output
}
}
let courseName = "Bachelor of Tourism Administration(B.T.A)".abbreviate()
print(courseName) // prints BTA
A clean approach would be:
extension Character
{
public func isUpper() -> Bool
{
let characterString = String(self)
return (characterString == characterString.uppercaseString) && (characterString != characterString.lowercaseString)
}
}
let courseName = "Bachelor of Tourism Administration(B.T.A)"
let upperCaseCourseName = courseName
let extrctCourseName = upperCaseCourseName.componentsSeparatedByString(" ").reduce("") {
if($0.1.characters.first!.isUpper()) {
return $0.0 + String($0.1.characters.first!)
}else {
return $0.0
}
}

NSPredicate filter array using all characters before delimiter

I am currently filtering using
self.searchArray.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (Array(exampleArray) as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.searchArray = array as! [String]
It is filtering the whole string, However, I only want to filter using all characters that exist before my delimiter. For example:
each array value has a delimiter which is "$%^"
example array contains [abc$%^12], [efg$%^32], [tyh$%^77]
I only want the filtering to include on all characters before $%^ which would be abc, efg, and tyh
You can do this without using NSPredicate. Stub:
let searchBarText = "h"
let exampleArray = ["abc$%^12", "efg$%^32", "tyh$%^77"]
let searchResults = exampleArray.filter {
let components = $0.componentsSeparatedByString("$%^")
return components[0].containsString(searchBarText)
}
'pure' Swift solution :-) (I don't like any help of Foundation)
let arr = ["abc$%^12", "efg$%^32", "tyh$%^77", "nodelemiter"]
let delimiter = "$%^"
func splitByDelimeter(let str: String, delimiter: String)->(String,String) {
var str1 = ""
var str2 = str
while !str2.hasPrefix(delimiter) && str2.characters.count > 0 {
str1.append(str2.removeAtIndex(str2.startIndex))
}
if (str1 == str) { return (str1, "") }
let r = Range(start: str2.startIndex, end: str2.startIndex.advancedBy(delimiter.characters.count))
str2.removeRange(r)
return (str1, str2)
}
let res = arr.map { splitByDelimeter($0, delimiter: delimiter).0 }
print(res) // ["abc", "efg", "tyh", "nodelemiter"]
let res2 = arr.map { splitByDelimeter($0, delimiter: delimiter) }
print(res2) // [("abc", "12"), ("efg", "32"), ("tyh", "77"), ("nodelemiter", "")]
it should work, even there is only delimiter, or more than one delimiter in your string. First occurrence of delimiter will split the string to two parts.
UPDATE
with help of String extension you can do it as ...
let arr = ["abc$%^12", "efg$%^32", "tyh$%^77", "nodelemiter", "$%^jhdk$%^jdhjahsd", "22lemar$%^fralemdo"]
extension String {
func splitBy(delimiter: String)->(String,String) {
var str1 = ""
var str2 = self
while !str2.hasPrefix(delimiter) && str2.characters.count > 0 {
str1.append(str2.removeAtIndex(str2.startIndex))
}
if (str1 == self) { return (str1, "") }
let r = Range(start: str2.startIndex, end: str2.startIndex.advancedBy(delimiter.characters.count))
str2.removeRange(r)
return (str1, str2)
}
func contains(string: String)->Bool {
guard !self.isEmpty else {
return false
}
var s = self.characters.map{ $0 }
let c = string.characters.map{ $0 }
repeat {
if s.startsWith(c){
return true
} else {
s.removeFirst()
}
} while s.count > c.count - 1
return false
}
}
let filtered = arr.map{ $0.splitBy("$%^").0 }.filter { $0.contains("lem") }
print(filtered) // ["nodelemiter", "22lemar"]
or you can use hasPrefix to narrow search by typing more chars to you search box ... etc.
let arr2 = ["abc$%^12", "abefg$%^32", "tyhab$%^77"]
let filtered2 = arr2.map{ $0.splitBy("$%^").0 }.filter { $0.hasPrefix("ab") }
print(filtered2) // ["abc", "abefg"]

Resources