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")
}
Related
struct Test {
var title: String
var message: [String?: String?]
init(title: String, message: [String?:String?]) {
self.title = title
self.message = message
}
}
var cases = [
Test(title: "1", message: ["tag1": nil]),
Test(title: "2", message: ["tag2": "preview2"]),
Test(title: "3", message: [nil:nil]),
Test(title: "4", message: ["tag1":"preview4"])
]
Now, I want:
An array with all keys from message property from cases - tag1 and tag2 (no nils in it). I just tried everything I know, I couldn't do it. Tried with filtering cases, got optionals.
There are no previews without a tag, so there is no need for an array with them. I only need a list with the tags, in order to sort it and show the relevant previews from the cases. That's why I need to know a way how to access these previews from the cases. Let's say in a UITableView:
cell.previewLabel?.text = cases[indexPath.row].preview[//No idea what here]
Of course, a dictionary with [tags: previews] would also be perfect!
Thanks in advance! I hope what I want is possible.
Here is an array that only contains elements from cases that have all keys and values not nil :
let filtered = cases.filter { test in
return test.message.allSatisfy({ entry in
return entry.key != nil && entry.value != nil
})
}
Or using the shorthand notation :
let filtered = cases.filter {
$0.message.allSatisfy({
$0.key != nil && $0.value != nil
})
}
With structs there is a default initializer, so you can write your Test struct this way:
struct Test {
var title: String
var message: [String?: String?]
}
It's not completely clear to me what you're attempting to do, however, this will filter your cases array to only Test objects that contain non-nil values in the message dictionary:
let nonNil = cases.filter { (test) -> Bool in
return Array(test.message.values).filter({ (value) -> Bool in
return value == nil
}).count <= 0
}
The variable nonNil now contains the Test objects where title is "2" and title is "4".
You could further filter that if you want a [tags:preview] dictionary. Something like this would do that:
let tags = nonNil.map( { $0.message} ).flatMap { $0 }.reduce([String:String]()) { (accumulator, current) -> [String:String] in
guard let key = current.key, let value = current.value else { return accumulator }
var accum = accumulator
accum.updateValue(value, forKey: key)
return accum
}
The tags dictionary now contains: ["tag1": "preview4", "tag2": "preview2"]
(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"]]
I am trying to create an array of words from a string object retrieved from Parse. The object retrieved looks like this:
Then this line of code gives this.
let joinedWords = object["Words"] as! String
How do I convert joinedWords to an Array?
If you don't care about the order, you can use flatMap on the set:
var mySet = Set<String>()
for index in 1...5 {
mySet.insert("testwords\(index)")
}
let myArray = mySet.flatMap { $0 }
print(myArray) // "["testwords5", "testwords3", "testwords4", "testwords2", "testwords1"]"
If you want the list sorted alphabetically, you can make your array a var and use sortInPlace()
var myArray = mySet.flatMap { $0 }
myArray.sortInPlace()
print(myArray) // "["testwords1", "testwords2", "testwords3", "testwords4", "testwords5"]"
If object["Words"] is AnyObject, you will have to unwrap it.
if let joinedWordsSet = object["Words"] as? Set<String> {
var joinedWordsArray = joinedWordsSet.flatMap { $0 }
myArray.sortInPlace()
print(myArray)
}
Swift 3 note: sortInPlace() has been renamed sort().
Many thanks to #JAL for so much time on chat to solve this one. This is what we came up with. Its a bodge and no doubt there is a better way!
When uploading to Parse save the set as an array.
let wordsSet = (wordList?.words?.valueForKey("wordName"))! as! NSSet
let wordsArray = Array(wordsSet)
Then it saves to Parse - looking like a set, not an array or a dictionary.
let parseWordList = PFObject(className: "WordList")
parseWordList.setObject("\(wordsArray)", forKey: "Words")
parseWordList.saveInBackgroundWithBlock { (succeeded, error) -> Void in
if succeeded {
// Do something
} else {
print("Error: \(error) \(error?.userInfo)")
}
}
Then you can drop the [ ] off the string when its downloaded from Parse, and remove the , and add some "" and voila, there is an array that can be used e.g. to add to CoreData.
var joinedWords = object["Words"] as! String
joinedWords = String(joinedWords.characters.dropFirst())
joinedWords = String(joinedWords.characters.dropLast())
let joinedWordsArray = joinedWords.characters.split() {$0 == ","}.map{ String($0) } // Thanks #JAL!
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
I need some help filtering an array of Structs.
This is what I am doing currently, it filters the array but not correctly.
For example lets say I search for an item in the array with "Mid" I have one item that should be shown however the item shown starts with "Bad".
var array = breweries.filter() { $0.name?.lowercaseString.rangeOfString(searchController.searchBar.text.lowercaseString) != nil }
results = array
here is my Struct
struct Breweries {
let name: String?
let breweryId: String?
let distance: Double?
let largeIconURL: String?
let streetAddress: String?
let locality: String?
let region: String?
let phone: String?
let website: String?
init(brewDictionary: [String: AnyObject]) {
name = brewDictionary["brewery"]?["name"] as? String
breweryId = brewDictionary["breweryId"] as? String
distance = brewDictionary["distance"] as? Double
largeIconURL = brewDictionary["brewery"]?["images"]??.objectForKey("large") as? String
streetAddress = brewDictionary["streetAddress"] as? String
locality = brewDictionary["locality"] as? String
region = brewDictionary["region"] as? String
phone = brewDictionary["phone"] as? String
website = brewDictionary["website"] as? String
}
}
Please point in the right direction!
Note: I am using Swift 1.2
Update:
I thought a video would be of help to better explain what I am trying to do.
Demo Of issue
What I want is to find the filter the array so only the item with a similar name is shown.
Update 2: As it turns out I forgot to handle the case when my UISearchController was active.
Assuming your Struct name is Breweries and it has a name property, try this:
let array = breweries.filter() {
($0.name!.lowercaseString as NSString).containsString(searchController.searchBar.text.lowercaseString)
}
Your usage of filter is correct, but your closure seem to be complicated with no clear goal. I suggest you to write an extension (or possibly use what I am using):
extension String {
func contains(search: String, ignoreCase: Bool = false, ignoreDiacritic: Bool = false) -> Bool {
var options = NSStringCompareOptions.allZeros
if ignoreCase { options |= NSStringCompareOptions.CaseInsensitiveSearch }
if ignoreDiacritic { options |= NSStringCompareOptions.DiacriticInsensitiveSearch }
return self.rangeOfString(search, options: options) != nil
}
}
This way you can use closure like this to search:
breweries.filter() {
$0.name?.contains("x") // Precise search
$0.name?.contains("x", ignoreCase: true, ignoreDiacritics: true) // Ignores diacritics and lower / upper case
}
of course, you can use | or & to search for multiple parameters
breweries.filter() {
$0.name?.contains("x") || $0.streetAddress?.contains("x")
}
Hope it helps!
Here is an example from an investing app with struct:
import Foundation
public struct SNStock {
public let ticker:NSString
public let name:NSString
init(ticker:NSString, name:NSString) {
self.ticker = ticker
self.name = name
}
}
Search on Main Thread:
public func searchStocksByKeyword(keyword:String) -> [SNStock] {
let lowercaseKeyword = keyword.lowercaseString
var searchResults:[SNStock] = []
searchResults = stocks.filter({ (stock:SNStock) -> Bool in
return stock.ticker.lowercaseString.hasPrefix(lowercaseKeyword)
})
if (searchResults.count == 0) {
searchResults = stocks.filter({ (stock:SNStock) -> Bool in
return stock.name.lowercaseString.hasPrefix(lowercaseKeyword)
})
}
searchResults.sortInPlace {
($0.ticker as String) < ($1.ticker as String)
}
return searchResults;
}
Search on Background Thread:
public func searchStocksByKeyword(keyword:String, completion:(stocks:[SNStock])->()) {
let qualityOfServiceClass = QOS_CLASS_USER_INTERACTIVE
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
let stocks:[SNStock] = self.searchStocksByKeyword(keyword)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
completion(stocks: stocks)
})
})
}