Regular Expression Check: return boolean and answers - arrays

(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"]]

Related

Need to get certain words from a long string swift

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)

How to compare elements of two arrays

I want to compare the elements of two arrays and check if they are equal.
I already tried various solutions but nothing really works.
I tried the solution from
How to compare two array of objects?
This is my object:
struct AccountBalance: Decodable {
let balance: Double
let currency: String
init(balance: Double, currency: String ) {
self.currency = currency
self.balance = balance
}
enum CodingKeys: String, CodingKey {
case currency = "Currency"
case balance = "Balance"
}
}
This is the code from the link I tried:
let result = zip(accountBalance, getsSaved).enumerate().filter() {
$1.0 == $1.1
}.map{$0.0}
But I get this error:
Closure tuple parameter '(offset: Int, element: (AccountBalance, AccountBalance))' does not support destructuring with implicit parameters
Array provides a function elementsEqual which is able to compare two arrays without explicitly conforming to Equatable:
let result = accountBalance.elementsEqual(getsSaved) {
$0.balance == $1.balance && $0.currency == $1.currency
}
Edit:
If you want to have the equality result regardless of the order of objects in the array then you can just add sort with each of the arrays.
let result = accountBalance.sorted { $0.balance < $1.balance }.elementsEqual(getsSaved.sorted { $0.balance < $1.balance }) {
$0.balance == $1.balance && $0.currency == $1.currency
}
I am guessing two arrays should be considered equal when they contain the same elements, regardless of ordering.
First, implement Equatable and Hashable.
I am using hashValue as an id so I can sort the arrays first.
Here is what your AccountBalance class should look like:
struct AccountBalance: Decodable, Equatable, Hashable {
// Important parts!
var hashValue: Int{
return balance.hashValue ^ currency.hashValue &* 1677619
}
static func == (lhs: AccountBalance, rhs: AccountBalance) -> Bool{
return lhs.balance == rhs.balance && lhs.currency == rhs.currency
}
}
Then create an algorithm that sorts the ararys and then check each elements by one by by if the contents are the same.
Here is the function that take use of Equatable and Hashable.
func isEqual(arr1: [AccountBalance], arr2: [AccountBalance]) -> Bool{
if arr1.count != arr1.count{
return false
}
let a = arr1.sorted(){
$0.hashValue > $1.hashValue
}
let b = arr2.sorted(){
$0.hashValue > $1.hashValue
}
let result = zip(a, b).enumerated().filter() {
$1.0 == $1.1
}.count
if result == a.count{
return true
}
return false
}
I will suggest you implement the accepted answer of the link you provided because it controls that both arrays are the same size and it orders them.
But if you want that your code works I solved like this:
In order to have control of the comparison your struct should implement the Equatable protocol and overload the operator ==
extension AccountBalance : Equatable {}
func ==(lhs: AccountBalance, rhs: AccountBalance) -> Bool {
return lhs.balance == rhs.balance && lhs.currency == rhs.currency
}
Then compare both arrays and check if contains false, if it does, one or more items in the array aren't the same.
let result = !zip(accountBalance, getsSaved).enumerated().map() {
$1.0 == $1.1
}.contains(false)
Hope it helps you
I'm not sure about what the rest of your code does, but making the parameters explicit does make XCode happy:
let result = zip(accountBalance, getsSaved).enumerated().filter() { (arg) -> Bool in
let (_, (balance1, balance2)) = arg
return balance1.balance == balance2.balance
}.map{ $0.0 }`
Tried (_, (balance1, balance2)) -> Bool in directly, but it wouldn't let me either
Sample with several arrays:
import Foundation
let arr1: [String?] = ["word", nil, "word3", "word4"]
let arr2: [Double?] = [1.01, 1.02, nil, 1.04]
let arr3: [Int?] = [nil, 2, 3, 4]
var tuple1: [(title: String, number: String, coord: String)] = []
let countArray = arr1.count
for element in 0..<countArray {
tuple1.append((arr1[element].map
{String($0)} ?? "", arr2[element].map
{String($0)} ?? "", arr3[element].map
{String($0)} ?? ""))
}
print(tuple1)
result of printing:
[(title: "word1", number: "1.01", coord: ""), (title: "", number: "1.02", coord: "2"), (title: "word3", number: "", coord: "3"), (title: "word4", number: "1.04", coord: "4")]

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

SWIFT String? does not have a member named 'element'

I would like to know how can i fill my label from an Array
func metaDataUpdated(metaData : NSString){
var listItems: NSArray = [metaData.componentsSeparatedByString(";")]
if ([listItems.count] > 0){
println([listItems.objectAtIndex(0)])
titleSong.text = [listItems.objectAtIndex(0)]
}
}
I don't really know how to convert an array to string.
Direct conversion to Swift:
func metaDataUpdated(metaData : String) {
let listItems = metaData.componentsSeparatedByString(";")
if listItems.count > 0 {
print(listItems[0])
titleSong.text = listItems[0]
}
}
Nicer Swift:
func metaDataUpdated(metaData : String) {
let listItems = metaData.componentsSeparatedByString(";")
if let first = listItems.first {
print(first)
titleSong.text = first
}
}
Even nicer Swift, without using Foundation and without the function needing to get every component separated by ";", but only the first one (recommended):
func metaDataUpdated(metaData : String) {
if let index = metaData.characters.indexOf(";") {
let first = metaData[metaData.startIndex ..< index]
print(first)
titleSong.text = first
}
}
you cannot assign NSArray to NSString therefore you need to cast the value of this first index into a string
change this
titleSong.text = [listItems.objectAtIndex(0)]
to
titleSong.text = "\(listItems.objectAtIndex(0))"
or
titleSong.text = listItems[0] as! String
and also change this line to ([listItems.count > 0]) to (listItems.count > 0)
your code will look like this:
Note this not obj-c so remove all []
func metaDataUpdated(metaData : NSString){
var listItems: NSArray = metaData.componentsSeparatedByString(";")
if (listItems.count > 0)
{
println(listItems.objectAtIndex(0))
titleSong.text = listItems.objectAtIndex(0) as! String
}
}
Better use Swift types and objects now: Array instead of NSArray, Dictionary instead of NSDictionary, etc.
func metaDataUpdated(metaData : NSString) {
var listItems = metaData.componentsSeparatedByString(";")
if listItems.count > 0 {
print(listItems[0])
titleSong.text = listItems[0]
}
}
Here componentsSeparatedByString returns an array of strings: [String]. We then use simple index subscripting to retrieve its first value.
Note: I suppose you were trying to adapt code from Objective-C because your example was ridden with [] everywhere...
Put your to string item in "\\()".
For instance:
titleSong.text = "\\([listItems.objectAtIndex(0)])"
Not sure you need the [] brackets though

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