Need to get certain words from a long string swift - arrays

So I have a this long text string and I need to get the words that are in the { } so I can put these in an array.
This is the string: <p style=\"text-align:center\">There will be some text here.<br><br>Send More Text in here<br><br>${LogoSquare} ${WUSquare} ${VisaSquare}</p>"
I have a regEx to see when I've reach the words that I need:
static let regEX = "^.*\\b(LogoSquare|WUSquare|VisaSquare)\\b.*$"
With the following function
fun containsValues(text: String) -> Bool {
if let regex = try? NSRegularExpression(pattern: AdviceCardRegexPattern.mytd, options: [.dotMatchesLineSeparators]) {
return (regex.firstMatch(in: text, options: [], range: NSRange(location: 0, length: text.count)) != nil)
}
return false
}
This is what I've tried so far:
var components = textPayload.components(separatedBy: ">")
I do the above so that the values get separated from the last > so it becomes like this
"${LogoSquare} ${WUSquare} ${VisaSquare}</p", ""]
Then I have:
let removal: [Character] = ["$", "{", "}", "<", "/"]
var imageTags: [String] = []
for value in components {
if containsValues(text: value) {
imageTags.append(value.filter { !removal.contains($0)})
}
}
This prints the following:
["LogoSquare WUSquare VisaSquarep"]
The only thing that's left is getting rid of the p at the end of VisaSquare

You're over-thinking this. Just match against ${something} and capture the something. For instance:
let s = "hey nonny nonny ${LogoSquare} ${WUSquare} ${VisaSquare} yoho yo"
let r = try! NSRegularExpression(pattern: "\\$\\{(.*?)\\}")
var result = [String]()
let ss = s as NSString
for m in r.matches(in: s, options: [], range: NSMakeRange(0, ss.length)) {
result.append(ss.substring(with: m.range(at: 1)))
}
print(result)

Related

How to subtract each Int value based on some condition in an array by a constant in Swift?

I have an array ["11-16,20-23", "11-16,20-23", "11-16,20-23"]
How can I subtract Int value which is greater than 12 of this array in Swift?
So array becomes ["11-4,8-11", "11-4,8-11", "11-4,8-11"] like this
my current workaround is
func test() {
let hours = "11-16,20-23|11-16,20-23|11-16,20-23|11-16,20-23|13-17,20-24|11-16,20-23|11-16,20-23"
var firstHalf : [String] = []
var secondHalf : [String] = []
let split = hours.components(separatedBy: "|")
for i in 0..<split.count {
let index = split[i].components(separatedBy: ",")
firstHalf.append(index[0])
secondHalf.append(index[1])
}
print(firstHalf)
print(secondHalf)
let final = firstHalf.first?.components(separatedBy: "-")
print(final?[0])
print(final?[1])
}
It is possible to do this using .map. Firstly if we just start with your data in an array
let data = ["11-16,20-23", "11-16,20-23", "11-16,20-23"]
We need to perform the following steps.
Split each String in the array by ","
Split the substrings from the above result by the "-"
Check to see if the value is greater than 12, subtract 12 if needed
Join the convert the new values with a "-"
Join the result from above with a ","
When we use split on a String we get the following [String.SubSequence], which we need to convert back into a [String] we can do this by performing a .map { String($0) } immediately after performing the split.
let data = ["11-16,20-23", "11-16,20-23", "11-16,20-23"]
let result = data.map { $0
.split(separator: ",") // split each String item at the comma
.map { String($0) } // convert to String as we have subsequences
.map { $0
.split(separator: "-") // Now split each string by the dash
.map { String($0) } // convert to String
.map(convertTime) // use the above convert time function
.joined(separator: "-") // join the values with a dash
}
.joined(separator: ",") // join the values with a comma
}
func convertTime(_ hour: String) -> String {
// When casting to Int we get Int? so we need to unwrap it
if let value = Int(hour), value > 12 {
return String(value - 12)
} else {
return hour
}
}
print(result) // ["11-4,8-11", "11-4,8-11", "11-4,8-11"]
You could make it simpler by using .components(separatedBy:)
let result = data.map { $0
.components(separatedBy: ",")
.map { $0
.components(separatedBy: "-")
.map(convertTime)
.joined(separator: "-")
}
.joined(separator: ",")
}
Update
Mapping to days of the week is possible. There are a couple of things that we need. Firstly, we need to make sure that the order of the data matches the order of the days of the week. If they do not match, or are not consistently ordered in the same way, then it would not be possible to map them.
Secondly, from your comment it looks like you want map them to a custom struct. So we need to create the struct
struct OpeningTimes {
let day: String
let fromWorkingHours: String
}
Then we can use our answer from above which outputs the updated hours to result and we can zip it with an array of the days of the week. For ease I have just used the output from the above in the code below so that you have a contained example.
let result = ["11-4,8-11", "11-4,8-11", "11-4,8-11", "11-4,8-11", "11-4,8-11", "11-4,8-11", "11-4,8-11"]
let weekDays = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
let arrayOfOpeningTimes = zip(weekDays, result).map {
OpeningTimes(day: $0.0, fromWorkingHours: $0.1)
}
print(arrayOfOpeningTimes)
This will create an array of OpeningTimes, just make sure that you have the same number of items in each array of week days and hours, otherwise there will be missing values.
To avoid code that is hard to understand or to long I think it's best to create to functions to handle part of the logic
//Handle the inner pair of open/close hours
func openHours(from string: String.SubSequence) -> (Int, Int)? {
let values = string.split(separator: "-")
.compactMap { Int($0) }
.map { $0 > 12 ? $0 - 12 : $0 }
guard values.count == 2 else { return nil }
return (values[0], values[1])
}
//Handle am and pm opening hours string
func dayHours(from string: String.SubSequence) -> String {
return string.split(separator: ",")
.compactMap(openHours)
.map { "\($0.0)-\($0.1)"}
.joined(separator: ", ")
}
Then the actual conversion becomes quite simple
let output = hours.split(separator: "|")
.map(dayHours)
To return this using a 12h format we need to change the openHours function
func openHours(from string: String.SubSequence) -> (String, String)? {
let values = string.split(separator: "-")
.compactMap { Int($0) }
.map { $0 > 12 ? "\($0 - 12)pm" : "\($0)am" }
guard values.count == 2 else { return nil }
return (values[0], values[1])
}
func test() {
let hours = "11-16,20-23|11-4,6-5|11-16,20-23|11-16,20-23|13-17,20-24|11-16,20-23|11-16,20-23"
let split = hours.components(separatedBy: "|")
var finalarray = [String]()
for i in 0..<split.count {
let splitedArray = split[i].components(separatedBy: ",")
var array = [String]()
for item in splitedArray {
let tmpstr = item.split(separator: "-").compactMap {
Int($0)
}.map { "\($0 > 12 ? $0 - 12 : $0)"}.joined(separator: "-")
array.append(tmpstr)
}
let str = array.joined(separator: ",")
finalarray.append(str)
}
print(finalarray.joined(separator: "|"))
}

Detect if string contains any element of a string array

How would I detect if a string contains any member of an array of strings (words)?
Here is the array:
let str:String = "house near the beach"
let wordGroups:[String] = ["beach","waterfront","with a water view","near ocean","close to water"]
The following is not compiling
let match:Bool = wordGroups.contains(where: str.contains)
I am using String extension:
extension String {
func contains(_ strings: [String]) -> Bool {
strings.contains { contains($0) }
}
}
Use case:
let str = "house near the beach"
let wordGroups = ["beach","waterfront", "with a water view", "near ocean", "close to water"]
let haveWord = str.contains(wordGroups)
In additional to answer of #Sh_Khan, if you want match some word from group:
let str:String = "house near the beach"
let wordGroups:[String] = ["beach","waterfront","with a water view","near ocean","close to water"]
let worlds = wordGroups.flatMap { $0.components(separatedBy: " ")}
let match = worlds.filter { str.range(of:$0) != nil }.count != 0
You can try
let str = Set("house near the beach")
let match = wordGroups.filter { str.contains($0) }.count != 0

Regular Expression Check: return boolean and answers

(Make it short to help others)
check if a string matches to a regex:
text.range(of: regex, options: .regularExpression) == nil
check string selected part matches:
let nsString = self as NSString
let regex = try NSRegularExpression(pattern: pattern)
let allMatches = regex
.matches(in: self, options: [], range: NSRange(location: 0, length: nsString.length))
return allMatches.map { match in
return (1 ..< match.numberOfRanges)
.map { matchIndex in
return nsString.substring(with: match.range(at: matchIndex))
}
}
The first one is easy and we don't actually need NSRegularExpression for that:
extension String {
func hasMatches(_ pattern: String) -> Bool {
return self.range(of: pattern, options: .regularExpression) != nil
}
}
let regex = "(.*) [:] (.*)"
let string = "Tom : how are you?"
print(string.hasMatches(regex))
I would say that we don't even need a utility function for that.
The second is harder to understand, mostly because NSRegularExpression API is not really converted to Swift and it even uses old NSString:
extension String {
func getMatches(_ pattern: String) throws -> [[String]] {
let nsString = self as NSString
let expression = try NSRegularExpression(pattern: pattern)
let matches = expression
.matches(in: self, options: [], range: NSRange(location: 0, length: nsString.length))
return matches.map { match in
let numGroups = match.numberOfRanges
// we are skipping group 0 which contains the pattern itself
return (1 ..< numGroups)
.map { groupIndex in
return nsString.substring(with: match.range(at: groupIndex))
}
}
}
}
print(try! string.getMatches(regex)) // [["Tom", "how are you?"]]
Note that I am returning an array of arrays because the expression can match multiple times.
For example:
let regex = "(\\d+):(\\d+)"
let string = "01:23, 02:34"
print(try! string.getMatches(regex)) // [["01", "23"], ["02", "34"]]

Search for exact match in string based on values in array

I am trying to search for items in an array using a free text string. I have got so far, see code below, but I'm still returning results which only partially match.
var townArray = ["Leamington Spa", "Birmingham", "Coventry", "Leamington Hastings", "Royal Leamington Spa", "Lea", "Leam", "Cove"]
let searchText = "Anyone want to meet in Leamington Spa or Coventry"
var resultsArray = [String]()
for i in townArray {
let range = searchText.lowercased().range(of: i.lowercased(), options: .regularExpression)
// (8 times)
if range != nil {
let found = searchText.substring(with: range!)
print(found)
resultsArray.append(found)
// (5 times)}
}
print(resultsArray)
// ["Leamington Spa", "Coventry", "Lea", "Leam", "Cove"]
Exact word matching is not a simple thing, you may need to use regular expression.
For example:
var townArray = ["Leamington Spa", "Birmingham", "Coventry", "Leamington Hastings", "Royal Leamington Spa", "Lea", "Leam", "Cove"]
let searchText = "Anyone want to meet in Leamington Spa or Coventry"
let townRegexArray = townArray.map {townName -> (name: String, regex: NSRegularExpression) in
let pattern = townName.components(separatedBy: " ")
.map{"\\b"+NSRegularExpression.escapedPattern(for: $0)+"\\b"}
.joined(separator: "\\s+")
//print(pattern) //->\bLeamington\b\s+\bSpa\b (=="\\bLeamington\\b\\s+\\bSpa\\b")...
let regex = try! NSRegularExpression(pattern: pattern, options: .caseInsensitive)
return (name: townName, regex: regex)
}
let resultsArray = townRegexArray
.filter{$0.regex.firstMatch(in: searchText, range: NSRange(0..<searchText.utf16.count)) != nil}
.map{$0.name}
print(resultsArray) //->["Leamington Spa", "Coventry"]
\b ("\\b" in String literal) represents word boundary in regex. So \bLeam\b does not match to Leamington.
You would do something like this
let matchingStrings = resultsArray.filter { $0 == "TARGET_STRING" }
let containsString = !matchingStrings.isEmpty

Check if array contains part of a string in Swift?

I have an array containing a number of strings. I have used contains() (see below) to check if a certain string exists in the array however I would like to check if part of a string is in the array?
itemsArray = ["Google, Goodbye, Go, Hello"]
searchToSearch = "go"
if contains(itemsArray, stringToSearch) {
NSLog("Term Exists")
}
else {
NSLog("Can't find term")
}
The above code simply checks if a value is present within the array in its entirety however I would like to find "Google, Google and Go"
Try like this.
let itemsArray = ["Google", "Goodbye", "Go", "Hello"]
let searchToSearch = "go"
let filteredStrings = itemsArray.filter({(item: String) -> Bool in
var stringMatch = item.lowercaseString.rangeOfString(searchToSearch.lowercaseString)
return stringMatch != nil ? true : false
})
filteredStrings will contain the list of strings having matched sub strings.
In Swift Array struct provides filter method, which will filter a provided array based on filtering text criteria.
First of all, you have defined an array with a single string.
What you probably want is
let itemsArray = ["Google", "Goodbye", "Go", "Hello"]
Then you can use contains(array, predicate) and rangeOfString() – optionally with
.CaseInsensitiveSearch – to check each string in the array
if it contains the search string:
let itemExists = contains(itemsArray) {
$0.rangeOfString(searchToSearch, options: .CaseInsensitiveSearch) != nil
}
println(itemExists) // true
Or, if you want an array with the matching items instead of a yes/no
result:
let matchingTerms = filter(itemsArray) {
$0.rangeOfString(searchToSearch, options: .CaseInsensitiveSearch) != nil
}
println(matchingTerms) // [Google, Goodbye, Go]
Update for Swift 3:
let itemExists = itemsArray.contains(where: {
$0.range(of: searchToSearch, options: .caseInsensitive) != nil
})
print(itemExists)
let matchingTerms = itemsArray.filter({
$0.range(of: searchToSearch, options: .caseInsensitive) != nil
})
print(matchingTerms)
Try like this.
Swift 3.0
import UIKit
let itemsArray = ["Google", "Goodbye", "Go", "Hello"]
var filterdItemsArray = [String]()
func filterContentForSearchText(searchText: String) {
filterdItemsArray = itemsArray.filter { item in
return item.lowercased().contains(searchText.lowercased())
}
}
filterContentForSearchText(searchText: "Go")
print(filterdItemsArray)
Output
["Google", "Goodbye", "Go"]
In Swift 5 with better readability :
let itemsArray = ["Google", "Goodbye", "Go", "Hello"]
let searchString = "Googled"
let result = itemsArray.contains(where: searchString.contains)
print(result) //prints true in the above case.
MARK:- Swift 5, Swift 4
//MARK:- You will find the array when its filter in "filteredStrings" variable you can check it by count if count > 0 its means you have find the results
let itemsArray = ["Google", "Goodbye", "Go", "Hello"]
let searchToSearch = "go"
let filteredStrings = itemsArray.filter({(item: String) -> Bool in
let stringMatch = item.lowercased().range(of: searchToSearch.lowercased())
return stringMatch != nil ? true : false
})
print(filteredStrings)
if (filteredStrings as NSArray).count > 0
{
//Record found
//MARK:- You can also print the result and can do any kind of work with them
}
else
{
//Record Not found
}
func filterContentForSearchText(_ searchText: String) {
filteredString = itemsArray.filter({( item : String) -> Bool in
return item.lowercased().contains(searchText.lowercased())
})
}
In Swift 4:
let itemsArray = ["Google", "Goodbye", "Go", "Hello"]
let searchString = "Go"
let filterArray = itemsArray.filter({ { $0.range(of: searchString, options: .caseInsensitive) != nil}
})
print(filterArray)
I had the same problem recently, didn't like most of these answers,
solved it like this:
let keywords = ["doctor", "hospital"] //your array
func keywordsContain(text: String) -> Bool { // text: your search text
return keywords.contains { (key) -> Bool in
key.lowercased().contains(text.lowercased())
}
}
This will also correctly trigger searches like "doc", which many of the above answers do not and is best practice.
contains() is more performant than first() != nil
source: https://www.avanderlee.com/swift/performance-collections/
If you are just checking if an item exists in a specific array, try this:
var a = [1,2,3,4,5]
if a.contains(4) {
print("Yes, it does contain number 4")
}
else {
print("No, it doesn't")
}

Resources