Extract a whole word from string In Swift - ios

for example I have a String text like : "I have to go to the kitchen"
and If I searched this text using the 'av' phrase I want a way that return me the whole word 'have'
how I can do this in swift

There is very nice solution with filter in swift.You can use rangeOfString method of String with filter to get only filtered string having "av"
var s = "I have to go to the kitchen"
//will return "have"
let abc:[String] = s.componentsSeparatedByString(" ").filter({ $0.rangeOfString("av", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil, locale: nil) != nil } )

There is an API for that, enumerateSubstrings(in:options:using:)
The byWords option returns all words in the closure
let string = "I have to go to the kitchen"
var found : String?
string.enumerateSubstrings(in: string.startIndex..., options: .byWords) { substring, _, _, stop in
if let word = substring, word.contains("av") {
found = word
stop = true
}
}
print(found ?? "not found")

Split your string into array by space char (" "), and return component that contains your 'av' string.

let words = stringYouWantToSearchIn.componentsSeparatedByString(" ")
for word in words
{
var range = word.rangeOfString(lastWord)
if (range != nil)
{
//you got what do you want in 'word variable'
break
}
}

Related

How to remove first word from a sentence in swift

I know how to remove first character from a word in swift like this:
var data = "CITY Singapore"
data.removeFirst()
print(data)//ITY Singapore
what i want is to remove the first word and space so the result is "Singapore".
How can i remove the first word and leading space in swift?
You can try
let data = "CITY Singapore"
let res = data.components(separatedBy: " ").dropFirst().joined(separator: " ")
print(res)
Or
let res = data[data.range(of: " ")!.upperBound...] // may crash for no " " inside the string
Or you can go with this too
let strData = "CITY Singapore"
if let data = strData.components(separatedBy: " ").dropFirst().first {
// do with data
}
else {
// fallback
}
This is a Regular Expression solution, the benefit is to modify the string in place.
The pattern searches from the beginning of the string to the first space character
var data = "CITY Singapore"
if let range = data.range(of: "^\\S+\\s", options: .regularExpression) {
data.removeSubrange(range)
}
You can use String's enumerateSubstrings in range method (Foundation) using byWords option and remove the first enclosing range. You need also to stop enumeration after removing the range at the first occurrence:
var string = "CITY Singapore"
string.enumerateSubstrings(in: string.startIndex..., options: .byWords) { (_, _, enclosingRange, stop) in
string.removeSubrange(enclosingRange)
stop = true
}
string // "Singapore"

backspace not work in outside of regex in swift

I use this method for patterning the phone number in UITextField at the .editingChange event
But the delete key only removes the numbers
extension String{
func applyPatternOnNumbers(pattern: String) -> String {
let replacmentCharacter: Character = "#"
let pureNumber = self.replacingOccurrences( of: "[^۰-۹0-9]", with: "", options: .regularExpression)
var result = ""
var pureNumberIndex = pureNumber.startIndex
for patternCharacter in pattern {
if patternCharacter == replacmentCharacter {
guard pureNumberIndex < pureNumber.endIndex else { return result }
result.append(pureNumber[pureNumberIndex])
pureNumber.formIndex(after: &pureNumberIndex)
} else {
result.append(patternCharacter)
}
}
return result
}
}
use at the editingChange event
let pattern = "+# (###) ###-####"
let mobile = textField.text.substring(to: pattern.count-1)
textfield.text = mobile.applyPatternOnNumbers(pattern: pattern)
// print(textfield.text) +1 (800) 666-8888
the problem is space & - , ( , ) chars can not to be removed
The RegEx you are trying is to not consider digits only:
[^۰-۹0-9]
I'm not sure, but you may change it to:
[^۰-۹0-9\s-\(\)]
and it may work. You might just add a \ before your special chars inside [] and you can any other chars into it that you do not need to be replaced.
Or you may simplify it to
[^\d\s-\(\)]
and it might work.
Method 2
You may use this RegEx which is an exact match to the phone number format you are having:
\+\d+\s\(\d{3}\)\s\d{3}-\d{4}
You may remove the first +, if it is unnecessary
\d+\s\(\d{3}\)\s\d{3}-\d{4}

How to add two Range<Index> together?

so I am filtering data (all strings), and want to show the following:
The search words where the first letters contain your search (example. when searching 'Br' I want to see all the words that begin with 'Br' like Break, Broke,...)
The search words where the string contains the word (example. When searching 'Br' is shows all words containing 'br' in the word like 'groundbreaker').
I've got both of them to work separately (see let found and let foundMore), but now I want to merge both of them (first showing the search words where the first letter contains your search, after the ones where the string contains the word). Tried using the addition sign, but it gives the following error
Binary operator '+' cannot be applied to two 'Range?' operands
extension SearchResultsController : UISearchResultsUpdating {
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.tableView.reloadData()
let searchBar = searchController.searchBar
let target = searchBar.text!
self.filteredData = self.originalData.filter {
s in
let options = NSStringCompareOptions.AnchoredSearch
let found = s.rangeOfString(target, options: options)
let optionsMore = NSStringCompareOptions.CaseInsensitiveSearch
let foundMore = s.rangeOfString(target, options: optionsMore)
let allTogether = found + foundMore
return (allTogether != nil)
}
self.tableView.reloadData()
}
}
Ranges cannot be added together, because the result may not be a proper range when the two ranges do not intersect.
However, you do not need to add this, because in the end you compare the overall range to nil. This means that you can compare the two sides to nil individually:
return s.rangeOfString(target, options: NSStringCompareOptions.AnchoredSearch) != nil
|| s.rangeOfString(target, options: NSStringCompareOptions.CaseInsensitiveSearch) != nil
check 1. and 2., should be viewed like this, not a mix of both
Then you should filter twice, and append the second array to first one:
var filtered1 = self.originalData.filter { s in
return s.rangeOfString(target, options: NSStringCompareOptions.AnchoredSearch) != nil
}
let filtered2 = self.originalData.filter { s in
// Exclude results of the first search
return s.rangeOfString(target, options: NSStringCompareOptions.AnchoredSearch) == nil
&& s.rangeOfString(target, options: NSStringCompareOptions.CaseInsensitiveSearch) != nil
}
filtered1 += filtered2
self.filteredData = filtered1
For your case you can replace + with ?? so that if either range is non-nil it will be set as the value of allTogether. This is minimal code, but isn't necessarily the most obvious or easy to read. An explicit logical OR nil test is more obvious.
If possible, can you can try this:
let indexSet = NSMutableIndexSet()
indexSet.addIndexesInRange(NSMakeRange(0, 5))
indexSet.addIndexesInRange(NSMakeRange(10, 4))
indexSet.addIndex(5)
println(indexSet)
Then you can do your activity here:
indexSet.enumerateIndexesUsingBlock { (index, stop) -> Void in
println(index)
}

Replace part of string with lower case letters - Swift

I have a Swift based iOS app and one of the features allows you to comment on a post. Anyway, users can add "#mentions" in their posts to tag other people. However I want to stop the user from adding a username with a capital letter.
Is there anyway I can convert a string, so that the #usernames are all in lowercase?
For example:
I really enjoy sightseeing with #uSerABC (not allowed)
I really enjoy sightseeing with #userabc (allowed)
I know there is a property for the string in swift called .lowercaseString - but the problem with that, is that it makes the entire string lowercase and thats not what I want. I only want the #username to be in lower case.
Is there any way around this with having to use the .lowercase property.
Thanks for your time, Dan.
This comes from a code I use to detect hashtags, I've modified to detect mentions:
func detectMentionsInText(text: String) -> [NSRange]? {
let mentionsDetector = try? NSRegularExpression(pattern: "#(\\w+)", options: NSRegularExpressionOptions.CaseInsensitive)
let results = mentionsDetector?.matchesInString(text, options: NSMatchingOptions.WithoutAnchoringBounds, range: NSMakeRange(0, text.utf16.count)).map { $0 }
return results?.map{$0.rangeAtIndex(0)}
}
It detects all the mentions in a string by using a regex and returns an NSRange array, by using a range you have the beginning and the end of the "mention" and you can easily replace them with a lower case version.
Split the string into two using the following command -
let arr = myString.componentsSeparatedByString("#")
//Convert arr[1] to lower case
//Append to arr[0]
//Enjoy
Thanks to everyone for their help. In the end I couldn't get any of the solutions to work and after a lot of testing, I came up with this solution:
func correctStringWithUsernames(inputString: String, completion: (correctString: String) -> Void) {
// Create the final string and get all
// the seperate strings from the data.
var finalString: String!
var commentSegments: NSArray!
commentSegments = inputString.componentsSeparatedByString(" ")
if (commentSegments.count > 0) {
for (var loop = 0; loop < commentSegments.count; loop++) {
// Check the username to ensure that there
// are no capital letters in the string.
let currentString = commentSegments[loop] as! String
let capitalLetterRegEx = ".*[A-Z]+.*"
let textData = NSPredicate(format:"SELF MATCHES %#", capitalLetterRegEx)
let capitalResult = textData.evaluateWithObject(currentString)
// Check if the current loop string
// is a #user mention string or not.
if (currentString.containsString("#")) {
// If we are in the first loop then set the
// string otherwise concatenate the string.
if (loop == 0) {
if (capitalResult == true) {
// The username contains capital letters
// so change it to a lower case version.
finalString = currentString.lowercaseString
}
else {
// The username does not contain capital letters.
finalString = currentString
}
}
else {
if (capitalResult == true) {
// The username contains capital letters
// so change it to a lower case version.
finalString = "\(finalString) \(currentString.lowercaseString)"
}
else {
// The username does not contain capital letters.
finalString = "\(finalString) \(currentString)"
}
}
}
else {
// The current string is NOT a #user mention
// so simply set or concatenate the finalString.
if (loop == 0) {
finalString = currentString
}
else {
finalString = "\(finalString) \(currentString)"
}
}
}
}
else {
// No issues pass back the string.
finalString = inputString
}
// Pass back the correct username string.
completion(correctString: finalString)
}
Its certainly not the most elegant or efficient solution around but it does work. If there are any ways of improving it, please leave a comment.

Parsing & contracting Russian full names

I have several text fields used to enter full name and short name, among other data. My task is:
Check if entered full name matches the standard Russian Cyrillic full name pattern:
Иванов Иван Иванович (three capitalized Cyrillic strings separated by spaces)
If it matches, create another string by auto-contracting full name according to pattern below and enter it to the corresponding text field:
Иванов И.И. (first string, space, first character of the second string, dot, first character of the third string, dot)
If it doesn't match, do nothing.
Currently I use the following code:
let fullNameArray = fullNameField.text!.characters.split{$0 == " "}.map(String.init)
if fullNameArray.count == 3 {
if fullNameArray[0] == fullNameArray[0].capitalizedString && fullNameArray[1] == fullNameArray[1].capitalizedString && fullNameArray[2] == fullNameArray[2].capitalizedString {
shortNameField.text = "\(fullNameArray[0]) \(fullNameArray[1].characters.first!).\(fullNameArray[2].characters.first!)."
}
}
How can I improve it? Maybe regular expressions could help me? If so, could you post some sample code?
My current solution:
do {
let regex = try NSRegularExpression(pattern: "^\\p{Lu}\\p{Ll}+\\s\\p{Lu}\\p{Ll}+\\s\\p{Lu}\\p{Ll}+$", options: .AnchorsMatchLines)
if regex.firstMatchInString(fullNameField.text!, options: [], range: NSMakeRange(0, fullNameField.text!.characters.count)) != nil {
let fullNameArray = fullNameField.text!.characters.split(" ").map(String.init)
shortNameField.text = "\(fullNameArray[0]) \(fullNameArray[1].characters.first!).\(fullNameArray[2].characters.first!)."
}
else {
shortNameField.text = ""
}
} catch let error as NSError {
print(error.localizedDescription)
}
Processes my full name pattern correctly.

Resources