kindly note that my question is specific to Swift 4 syntax. I have a bulky/large array of Strings and I want to filter it for getting all values in it which are started with some specific characters/substring. That means I need each String in my array which matches to started with some substring. I found different links which gives me code for Objective-C and I am unable to implement it in Swift 4 because of that methods are not available in Swift 4. I solved my question manually by iterating my array in for loop but it gives very slow result So I don't want to use any loop here, So any help will useful. Thanks in advance. See my code below:
func search() -> Void {
var dummyStringsArray:[String] = ["Hotel Restaurants","Restaurants","Certified Green Restaurant(R)","Japnies Food Restauarants","Grill Restaurants","Restaurant Equipment","Wholsale Restaurant Fixtures","American Food","Wholsale Restaurant Supplies","Veg Restaurants","Barbecue Restaurants","Non-Veg Restaurants"]
var displayDataArray:[String] = []
let searchString:NSString = (textField.text!).lowercased() as NSString
for string in self.dummyStringsArray {
let mainString:NSString = string.lowercased() as NSString
if mainString.length >= searchString.length {
let compareString = String(mainString.substring(to: searchString.length))
if searchString as String == compareString {
displayDataArray.append(string)
}
}
}
}
So if I entered text in textField as 're' then it should return displayDataArray containing values like "Restaurants", "Restaurant Equipment".
I think you can't do this without any loop because you need to go through all the elements
But I can offer you more elegant solution with filter function:
func search() -> Void {
let dummyStringsArray:[String] = ["Hotel Restaurants","Restaurants","Certified Green Restaurant(R)","Japnies Food Restauarants","Grill Restaurants","Restaurant Equipment","Wholsale Restaurant Fixtures","American Food","Wholsale Restaurant Supplies","Veg Restaurants","Barbecue Restaurants","Non-Veg Restaurants"]
let searchString: String = (textField.text!).lowercased()
let displayDataArray: [String] = dummyStringsArray.filter({ String($0.prefix(searchString.count)).lowercased() == searchString })
}
You will probably get the best performance using range(of:options:) while passing in the options of .caseInsensitive and .anchored.
func search() -> Void {
let dummyStringsArray = ["Hotel Restaurants","Restaurants","Certified Green Restaurant(R)","Japnies Food Restauarants","Grill Restaurants","Restaurant Equipment","Wholsale Restaurant Fixtures","American Food","Wholsale Restaurant Supplies","Veg Restaurants","Barbecue Restaurants","Non-Veg Restaurants"]
let searchString = textField.text!
let displayDataArray = dummyStringsArray.filter { $0.range(of: searchString, options: [ .caseInsensitive, .anchored ]) != nil }
}
I'm trying to implement search inside my app that I'm making. I have an array that I'm trying to search and I find this code online:
func filterContentForSearchText(searchText: String) {
filteredCandies = candies.filter({( candy : Candies) -> Bool in
if candy.name.lowercaseString.containsString(searchText.lowercaseString) == true {
return true
} else {
return false
}
})
tableView.reloadData()
}
The issue is that the database that I'm trying to implement search on has text that is all scrambled because it was supposed to shortened. How can I make it so that the search will check if all the letters are there instead of searching exactly the right name. Example of object from database (USDA): CRAB, DUNGINESS, RAW
If you have an answer, please make it fast enough for live searching. Non live searching makes searching terrible (at least for me)!
I'm using Swift 2.2 and Xcode 7
As an improvement to #appzYourLife's solution, you could do this with a native Swift Set, as a counted set isn't necessarily needed in this case. This will save having to map(_:) over the characters of each name and bridging them to Objective-C. You can now just use a set of Characters, as they're Hashable.
For example:
struct Candy {
let name: String
}
let candies = [Candy(name: "CRAB"), Candy(name: "DUNGINESS"), Candy(name: "RAW")]
var filteredCandies = [Candy]()
func filterContentForSearchText(searchText: String) {
let searchCharacters = Set(searchText.lowercaseString.characters)
filteredCandies = candies.filter {Set($0.name.lowercaseString.characters).isSupersetOf(searchCharacters)}
tableView.reloadData()
}
filterContentForSearchText("RA")
print(filteredCandies) // [Candy(name: "CRAB"), Candy(name: "RAW")]
filterContentForSearchText("ED")
print(filteredCandies) // Candy(name: "DUNGINESS")]
Also depending on whether you can identify this as a performance bottleneck (you should do some profiling first) – you could potentially optimise the above further by caching the sets containing the characters of your 'candy' names, saving from having to recreate them at each search (although you'll have to ensure that they're updated if you update your candies data).
When you come to search, you can then use zip(_:_:) and flatMap(_:) in order to filter out the corresponding candies.
let candies = [Candy(name: "CRAB"), Candy(name: "DUNGINESS"), Candy(name: "RAW")]
// cached sets of (lowercased) candy name characters
let candyNameCharacterSets = candies.map {Set($0.name.lowercaseString.characters)}
var filteredCandies = [Candy]()
func filterContentForSearchText(searchText: String) {
let searchCharacters = Set(searchText.lowercaseString.characters)
filteredCandies = zip(candyNameCharacterSets, candies).flatMap {$0.isSupersetOf(searchCharacters) ? $1 : nil}
tableView.reloadData()
}
First of all a block of code like this
if someCondition == true {
return true
} else {
return false
}
can also be written this ways
return someCondition
right? :)
Refactoring
So your original code would look like this
func filterContentForSearchText(searchText: String) {
filteredCandies = candies.filter { $0.name.lowercaseString.containsString(searchText.lowercaseString) }
tableView.reloadData()
}
Scrambled search
Now, given a string A, your want to know if another string B contains all the character of A right?
For this we need CountedSet which is available from Swift 3. Since you are using Swift 2.2 we'll use the old NSCountedSet but some bridging to Objective-C is needed.
Here's the code.
struct Candy {
let name: String
}
let candies = [Candy]()
var filteredCandies = [Candy]()
func filterContentForSearchText(searchText: String) {
let keywordChars = NSCountedSet(array:Array(searchText.lowercaseString.characters).map { String($0) })
filteredCandies = candies.filter {
let candyChars = NSCountedSet(array:Array($0.name.lowercaseString.characters).map { String($0) }) as Set<NSObject>
return keywordChars.isSubsetOfSet(candyChars)
}
tableView.reloadData()
}
Swift 3 code update :
func filterContentForSearchText(searchText: String, scope: String = "All") {
filteredCandies = candies.filter { candy in
return candy.name.localizedLowercase.contains(searchText.lowercased())
}
tableView.reloadData()
}
I'm compartmentalizing users on firebase according to their email domain. What universal code can I use to extract only 'havard' in the following examples? All user emails end in '.edu'
let email = jsmith#student.havard.edu ,
let email = jsmith#havard.edu
Here is a simple function that will work for the input set you provided.
func getMainPart(s: String) -> String {
let charSet = NSCharacterSet(charactersInString: ".#")
let v = s.componentsSeparatedByCharactersInSet(charSet)
let pos = v.count - 2
return v[pos]
}
Here's another solution:
func getMainPart2(s: String) -> String {
var v = s.componentsSeparatedByString("#").last?.componentsSeparatedByString(".")
v?.removeLast()
return (v!.last)!
}
You can call it like this:
let email1 = "smith#harvard.edu"
let s = getMainPart2(email1)
print(s) // this outputs: harvard
One legal email address has only one "#" so separate email address:
public func componentsSeparatedByString(separator: String) -> [String]
This is sample code:
let components = email.componentsSeparatedByString("#").last?.componentsSeparatedByString(".")
if let index = components?.endIndex.advancedBy(-2) {
components![index]
}
So I am trying to get the Actual Variable Name as String in Swift, but have not found a way to do so... or maybe I am looking at this problem and solution in a bad angle.
So this is basically what I want to do:
var appId: String? = nil
//This is true, since appId is actually the name of the var appId
if( appId.getVarName = "appId"){
appId = "CommandoFurball"
}
Unfortunately I have not been able to find in apple docs anything that is close to this but this:
varobj.self or reflect(var).summary
however, this gives information of what is inside the variable itself or the type of the variable in this case being String and I want the Actual name of the Variable.
This is officially supported in Swift 3 using #keyPath()
https://github.com/apple/swift-evolution/blob/master/proposals/0062-objc-keypaths.md
Example usage would look like:
NSPredicate(format: "%K == %#", #keyPath(Person.firstName), "Wendy")
In Swift 4 we have something even better: \KeyPath notation
https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md
NSPredicate(format: "%K == %#", \Person.mother.firstName, "Wendy")
// or
let keyPath = \Person.mother.firstName
NSPredicate(format: "%K == %#", keyPath, "Andrew")
The shorthand is a welcome addition, and being able to reference keypaths from a variable is extremely powerful
As per the updated from this answer, it is supported in Swift 3 via #keyPath
NSPredicate(format: "%K == %#", #keyPath(Person.firstName), "Andrew")
This is my solution
class Test {
var name: String = "Ido"
var lastName: String = "Cohen"
}
let t = Test()
let mirror = Mirror(reflecting: t)
for child in mirror.children {
print(child.label ?? "")
}
print will be
name
lastName
This works:
struct s {
var x:Int = 1
var y:Int = 2
var z:Int = 3
}
var xyz = s()
let m = Mirror(reflecting: xyz)
print(m.description)
print(m.children.count)
for p in m.children {
print(p.label as Any)
}
I've come up with a swift solution, however unfortunately it doesn't work with Ints, Floats, and Doubles I believe.
func propertyNameFor(inout item : AnyObject) -> String{
let listMemAdd = unsafeAddressOf(item)
let propertyName = Mirror(reflecting: self).children.filter { (child: (label: String?, value: Any)) -> Bool in
if let value = child.value as? AnyObject {
return listMemAdd == unsafeAddressOf(value)
}
return false
}.flatMap {
return $0.label!
}.first ?? ""
return propertyName
}
var mutableObject : AnyObject = object
let propertyName = MyClass().propertyNameFor(&mutableObject)
It compares memory addresses for an object's properties and sees if any match.
The reason it doesn't work for Ints, Floats, and Doubles because they're not of type anyobject, although you can pass them as anyobject, when you do so they get converted to NSNumbers. therefore the memory address changes. they talk about it here.
For my app, it didn't hinder me at all because I only needed it for custom classes. So maybe someone will find this useful. If anyone can make this work with the other datatypes then that would be pretty cool.
Completing the accepted answer for extensions:
The property needs to be #objc.
var appId: String? {
....
}
You need to use #keyPath syntax, \ notation is not supported yet for extensions.
#keyPath(YourClass.appId)
The best solution is Here
From given link
import Foundation
extension NSObject {
//
// Retrieves an array of property names found on the current object
// using Objective-C runtime functions for introspection:
// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
//
func propertyNames() -> Array<String> {
var results: Array<String> = [];
// retrieve the properties via the class_copyPropertyList function
var count: UInt32 = 0;
var myClass: AnyClass = self.classForCoder;
var properties = class_copyPropertyList(myClass, &count);
// iterate each objc_property_t struct
for var i: UInt32 = 0; i < count; i++ {
var property = properties[Int(i)];
// retrieve the property name by calling property_getName function
var cname = property_getName(property);
// covert the c string into a Swift string
var name = String.fromCString(cname);
results.append(name!);
}
// release objc_property_t structs
free(properties);
return results;
}
}
In Objective-C the code to check for a substring in an NSString is:
NSString *string = #"hello Swift";
NSRange textRange =[string rangeOfString:#"Swift"];
if(textRange.location != NSNotFound)
{
NSLog(#"exists");
}
But how do I do this in Swift?
You can do exactly the same call with Swift:
Swift 4 & Swift 5
In Swift 4 String is a collection of Character values, it wasn't like this in Swift 2 and 3, so you can use this more concise code1:
let string = "hello Swift"
if string.contains("Swift") {
print("exists")
}
Swift 3.0+
var string = "hello Swift"
if string.range(of:"Swift") != nil {
print("exists")
}
// alternative: not case sensitive
if string.lowercased().range(of:"swift") != nil {
print("exists")
}
Older Swift
var string = "hello Swift"
if string.rangeOfString("Swift") != nil{
println("exists")
}
// alternative: not case sensitive
if string.lowercaseString.rangeOfString("swift") != nil {
println("exists")
}
I hope this is a helpful solution since some people, including me, encountered some strange problems by calling containsString().1
PS. Don't forget to import Foundation
Footnotes
Just remember that using collection functions on Strings has some edge cases which can give you unexpected results, e. g. when dealing with emojis or other grapheme clusters like accented letters.
Extension way
Swift 4
extension String {
func contains(find: String) -> Bool{
return self.range(of: find) != nil
}
func containsIgnoringCase(find: String) -> Bool{
return self.range(of: find, options: .caseInsensitive) != nil
}
}
var value = "Hello world"
print(value.contains("Hello")) // true
print(value.contains("bo")) // false
print(value.containsIgnoringCase(find: "hello")) // true
print(value.containsIgnoringCase(find: "Hello")) // true
print(value.containsIgnoringCase(find: "bo")) // false
Generally Swift 4 has contains method however it available from iOS 8.0+
Swift 3.1
You can write extension contains: and containsIgnoringCase for String
extension String {
func contains(_ find: String) -> Bool{
return self.range(of: find) != nil
}
func containsIgnoringCase(_ find: String) -> Bool{
return self.range(of: find, options: .caseInsensitive) != nil
}
}
Older Swift version
extension String {
func contains(find: String) -> Bool{
return self.rangeOfString(find) != nil
}
func containsIgnoringCase(find: String) -> Bool{
return self.rangeOfString(find, options: NSStringCompareOptions.CaseInsensitiveSearch) != nil
}
}
Example:
var value = "Hello world"
print(value.contains("Hello")) // true
print(value.contains("bo")) // false
print(value.containsIgnoringCase("hello")) // true
print(value.containsIgnoringCase("Hello")) // true
print(value.containsIgnoringCase("bo")) // false
From the docs, it seems that calling containsString() on a String should work:
Swift’s String type is bridged seamlessly to Foundation’s NSString
class. If you are working with the Foundation framework in Cocoa or
Cocoa Touch, the entire NSString API is available to call on any
String value you create, in addition to the String features described
in this chapter. You can also use a String value with any API that
requires an NSString instance.
However, it doesn't seem to work that way.
If you try to use someString.containsString(anotherString), you will get a compile time error that states 'String' does not contain a member named 'containsString'.
So, you're left with a few options, one of which is to explicitly bridge your String to Objective-C by using bridgeToObjectiveC() other two involve explicitly using an NSString and the final one involves casting the String to an NSString
By bridging, you'd get:
var string = "hello Swift"
if string.bridgeToObjectiveC().containsString("Swift") {
println("YES")
}
By explicitly typing the string as an NSString, you'd get:
var string: NSString = "hello Swift"
if string.containsString("Swift") {
println("YES")
}
If you have an existing String, you can initialize an NSString from it by using NSString(string:):
var string = "hello Swift"
if NSString(string: string).containsString("Swift") {
println("YES")
}
And finally, you can cast an existing String to an NSString as below
var string = "hello Swift"
if (string as NSString).containsString("Swift") {
println("YES")
}
Another one. Supports case and diacritic options.
Swift 3.0
struct MyString {
static func contains(_ text: String, substring: String,
ignoreCase: Bool = true,
ignoreDiacritic: Bool = true) -> Bool {
var options = NSString.CompareOptions()
if ignoreCase { _ = options.insert(NSString.CompareOptions.caseInsensitive) }
if ignoreDiacritic { _ = options.insert(NSString.CompareOptions.diacriticInsensitive) }
return text.range(of: substring, options: options) != nil
}
}
Usage
MyString.contains("Niels Bohr", substring: "Bohr") // true
iOS 9+
Case and diacritic insensitive function available since iOS 9.
if #available(iOS 9.0, *) {
"Für Elise".localizedStandardContains("fur") // true
}
As of Xcode 7.1 and Swift 2.1 containsString() is working fine for me.
let string = "hello swift"
if string.containsString("swift") {
print("found swift")
}
Swift 4:
let string = "hello swift"
if string.contains("swift") {
print("found swift")
}
And a case insensitive Swift 4 example:
let string = "Hello Swift"
if string.lowercased().contains("swift") {
print("found swift")
}
Or using a case insensitive String extension:
extension String {
func containsIgnoreCase(_ string: String) -> Bool {
return self.lowercased().contains(string.lowercased())
}
}
let string = "Hello Swift"
let stringToFind = "SWIFT"
if string.containsIgnoreCase(stringToFind) {
print("found: \(stringToFind)") // found: SWIFT
}
print("string: \(string)")
print("stringToFind: \(stringToFind)")
// console output:
found: SWIFT
string: Hello Swift
stringToFind: SWIFT
In Swift 4.2
Use
func contains(_ str: String) -> Bool
Example
let string = "hello Swift"
let containsSwift = string.contains("Swift")
print(containsSwift) // prints true
> IN SWIFT 3.0
let str = "Hello Swift"
if str.lowercased().contains("Swift".lowercased()) {
print("String Contains Another String")
} else {
print("Not Exists")
}
Output
String Contains Another String
You can do this very easily in Swift using the code:
let string = "hello Swift";
let subString = (string as NSString).containsString("Swift")
if(subString){println("Exist")}
Of all of the answers here, I think they either don't work, or they're a bit of a hack (casting back to NSString). It's very likely that the correct answer to this has changed with the different beta releases.
Here is what I use:
let string: String = "hello Swift"
if string.rangeOfString("Swift") != nil
{
println("exists")
}
The "!= nil" became required with Beta 5.
Just an addendum to the answers here.
You can also do a local case insensitive test using:
- (BOOL)localizedCaseInsensitiveContainsString:(NSString *)aString
Example:
import Foundation
var string: NSString = "hello Swift"
if string.localizedCaseInsensitiveContainsString("Hello") {
println("TRUE")
}
UPDATE
This is part of the Foundation Framework for iOS & Mac OS X 10.10.x
and was part of 10.10 at Time of my original Posting.
Document Generated: 2014-06-05 12:26:27 -0700 OS X Release Notes
Copyright © 2014 Apple Inc. All Rights Reserved.
OS X 10.10 Release Notes Cocoa Foundation Framework
NSString now has the following two convenience methods:
- (BOOL)containsString:(NSString *)str;
- (BOOL)localizedCaseInsensitiveContainsString:(NSString *)str;
Here is my first stab at this in the swift playground.
I extend String by providing two new functions (contains and containsIgnoreCase)
extension String {
func contains(other: String) -> Bool{
var start = startIndex
do{
var subString = self[Range(start: start++, end: endIndex)]
if subString.hasPrefix(other){
return true
}
}while start != endIndex
return false
}
func containsIgnoreCase(other: String) -> Bool{
var start = startIndex
do{
var subString = self[Range(start: start++, end: endIndex)].lowercaseString
if subString.hasPrefix(other.lowercaseString){
return true
}
}while start != endIndex
return false
}
}
Use it like this
var sentence = "This is a test sentence"
sentence.contains("this") //returns false
sentence.contains("This") //returns true
sentence.containsIgnoreCase("this") //returns true
"This is another test sentence".contains(" test ") //returns true
I'd welcome any feedback :)
Here you are:
let s = "hello Swift"
if let textRange = s.rangeOfString("Swift") {
NSLog("exists")
}
You can just do what you have mentioned:
import Foundation
...
string.contains("Swift");
From the docs:
Swift’s String type is bridged seamlessly to Foundation’s NSString
class. If you are working with the Foundation framework in Cocoa or
Cocoa Touch, the entire NSString API is available to call on any
String value you create, in addition to the String features described
in this chapter. You can also use a String value with any API that
requires an NSString instance.
You need to import Foundation to bridge the NSString methods and make them available to Swift's String class.
You don't need to write any custom code for this. Starting from the 1.2 version Swift has already had all the methods you need:
getting string length: count(string);
checking if string contains substring: contains(string, substring);
checking if string starts with substring: startsWith(string, substring)
and etc.
In Swift 3
if((a.range(of: b!, options: String.CompareOptions.caseInsensitive, range: nil, locale: nil)) != nil){
print("Done")
}
Here you go! Ready for Xcode 8 and Swift 3.
import UIKit
let mString = "This is a String that contains something to search."
let stringToSearchUpperCase = "String"
let stringToSearchLowerCase = "string"
mString.contains(stringToSearchUpperCase) //true
mString.contains(stringToSearchLowerCase) //false
mString.lowercased().contains(stringToSearchUpperCase) //false
mString.lowercased().contains(stringToSearchLowerCase) //true
string.containsString is only available in 10.10 Yosemite (and probably iOS8).
Also bridging it to ObjectiveC crashes in 10.9. You're trying to pass a NSString to NSCFString. I don't know the difference, but I can say 10.9 barfs when it executes this code in a OS X 10.9 app.
Here are the differences in Swift with 10.9 and 10.10:
https://developer.apple.com/library/prerelease/mac/documentation/General/Reference/APIDiffsMacOSX10_10SeedDiff/index.html containsString is only available in 10.10
Range of String above works great on 10.9. I am finding developing on 10.9 is super stable with Xcode beta2. I don't use playgrounds through or the command line version of playgrounds. I'm finding if the proper frameworks are imported the autocomplete is very helpful.
Xcode 8/Swift 3 version:
let string = "hello Swift"
if let range = string.range(of: "Swift") {
print("exists at range \(range)")
} else {
print("does not exist")
}
if let lowercaseRange = string.lowercased().range(of: "swift") {
print("exists at range \(lowercaseRange)")
} else {
print("does not exist")
}
You can also use contains:
string.contains("swift") // false
string.contains("Swift") // true
Swift 4 way to check for substrings, including the necessary Foundation (or UIKit) framework import:
import Foundation // or UIKit
let str = "Oh Canada!"
str.contains("Can") // returns true
str.contains("can") // returns false
str.lowercased().contains("can") // case-insensitive, returns true
Unless Foundation (or UIKit) framework is imported, str.contains("Can") will give a compiler error.
This answer is regurgitating manojlds's answer, which is completely correct. I have no idea why so many answers go through so much trouble to recreate Foundation's String.contains(subString: String) method.
With and new syntax in swift 4 you can just
string.contains("Swift 4 is the best")
string is your string variable
Check if it contains 'Hello'
let s = "Hello World"
if s.rangeOfString("Hello") != nil {
print("Yes it contains 'Hello'")
}
Swift 5, case insensitive:
if string.localizedLowercase.contains("swift".localizedLowercase){
// Search string exist in employee name finding.
var empName:NSString! = employeeDetails[filterKeyString] as NSString
Case sensitve search.
let rangeOfSearchString:NSRange! = empName.rangeOfString(searchString, options: NSStringCompareOptions.CaseInsensitiveSearch)
// Not found.
if rangeOfSearchString.location != Foundation.NSNotFound
{
// search string not found in employee name.
}
// Found
else
{
// search string found in employee name.
}
Swift 3: Here you can see my smart search extension fro string that let you make a search on string for seeing if it contains, or maybe to filter a collection based on a search text.
https://github.com/magonicolas/Swift-Smart-String-Search
If you want to check that one String contains another Sub-String within it or not you can check it like this too,
var name = String()
name = "John has two apples."
Now, in this particular string if you want to know if it contains fruit name 'apple' or not you can do,
if name.contains("apple") {
print("Yes , it contains fruit name")
} else {
print("it does not contain any fruit name")
}
Hope this works for you.
In iOS 8 and newer, you can use these two NSString methods:
#availability(iOS, introduced=8.0)
func containsString(aString: String) -> Bool
#availability(iOS, introduced=8.0)
func localizedCaseInsensitiveContainsString(aString: String) -> Bool
I've found a couple of interesting use cases. These variants make use of the rangeOfString method and I include the equality example to show how one might best use the search and comparison features of Strings in Swift 2.0
//In viewDidLoad() I assign the current object description (A Swift String) to self.loadedObjectDescription
self.loadedObjectDescription = self.myObject!.description
Later after I've made changes to self.myObject, I can refer to the
following string comparison routines (setup as lazy variables that
return a Bool). This allows one to check the state at any time.
lazy var objectHasChanges : Bool = {
guard self.myObject != nil else { return false }
return !(self.loadedObjectDescription == self.myObject!.description)
}()
A variant of this happens when sometimes I need to analyze a missing
property on that object. A string search allows me to find a
particular substring being set to nil (the default when an object is created).
lazy var isMissingProperty : Bool = {
guard self.myObject != nil else { return true }
let emptyPropertyValue = "myProperty = nil"
return (self.myObject!.description.rangeOfString(emptyPropertyValue) != nil) ? true : false
}()
SWIFT 4 is very easy!!
if (yourString.contains("anyThing")) {
print("Exist")
}