How can we compare two strings in swift ignoring case ?
for eg :
var a = "Cash"
var b = "cash"
Is there any method that will return true if we compare var a & var b
Try this :
For older swift:
var a : String = "Cash"
var b : String = "cash"
if(a.caseInsensitiveCompare(b) == NSComparisonResult.OrderedSame){
println("Et voila")
}
Swift 3+
var a : String = "Cash"
var b : String = "cash"
if(a.caseInsensitiveCompare(b) == .orderedSame){
print("Et voila")
}
Use caseInsensitiveCompare method:
let a = "Cash"
let b = "cash"
let c = a.caseInsensitiveCompare(b) == .orderedSame
print(c) // "true"
ComparisonResult tells you which word comes earlier than the other in lexicographic order (i.e. which one comes closer to the front of a dictionary). .orderedSame means the strings would end up in the same spot in the dictionary
if a.lowercaseString == b.lowercaseString {
//Strings match
}
Try this:
var a = "Cash"
var b = "cash"
let result: NSComparisonResult = a.compare(b, options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil, locale: nil)
// You can also ignore last two parameters(thanks 0x7fffffff)
//let result: NSComparisonResult = a.compare(b, options: NSStringCompareOptions.CaseInsensitiveSearch)
result is type of NSComparisonResult enum:
enum NSComparisonResult : Int {
case OrderedAscending
case OrderedSame
case OrderedDescending
}
So you can use if statement:
if result == .OrderedSame {
println("equal")
} else {
println("not equal")
}
localizedCaseInsensitiveContains : Returns whether the receiver contains a given string by performing a case-insensitive, locale-aware search
if a.localizedCaseInsensitiveContains(b) {
//returns true if a contains b (case insensitive)
}
Edited:
caseInsensitiveCompare : Returns the result of invoking compare(_:options:) with NSCaseInsensitiveSearch as the only option.
if a.caseInsensitiveCompare(b) == .orderedSame {
//returns true if a equals b (case insensitive)
}
CORRECT WAY:
let a: String = "Cash"
let b: String = "cash"
if a.caseInsensitiveCompare(b) == .orderedSame {
//Strings match
}
Please note: ComparisonResult.orderedSame can also be written as .orderedSame in shorthand.
OTHER WAYS:
a.
if a.lowercased() == b.lowercased() {
//Strings match
}
b.
if a.uppercased() == b.uppercased() {
//Strings match
}
c.
if a.capitalized() == b.capitalized() {
//Strings match
}
Could just roll your own:
func equalIgnoringCase(a:String, b:String) -> Bool {
return a.lowercaseString == b.lowercaseString
}
For Swift 5
Ignoring the case and compare two string
var a = "cash"
var b = "Cash"
if(a.caseInsensitiveCompare(b) == .orderedSame){
print("Ok")
}
Phone numbers comparison example; using swift 4.2
var selectPhone = [String]()
if selectPhone.index(where: {$0.caseInsensitiveCompare(contactsList[indexPath.row].phone!) == .orderedSame}) != nil {
print("Same value")
} else {
print("Not the same")
}
You can just write your String Extension for comparison in just a few line of code
extension String {
func compare(_ with : String)->Bool{
return self.caseInsensitiveCompare(with) == .orderedSame
}
}
Swift 4, I went the String extension route using caseInsensitiveCompare() as a template (but allowing the operand to be an optional). Here's the playground I used to put it together (new to Swift so feedback more than welcome).
import UIKit
extension String {
func caseInsensitiveEquals<T>(_ otherString: T?) -> Bool where T : StringProtocol {
guard let otherString = otherString else {
return false
}
return self.caseInsensitiveCompare(otherString) == ComparisonResult.orderedSame
}
}
"string 1".caseInsensitiveEquals("string 2") // false
"thingy".caseInsensitiveEquals("thingy") // true
let nilString1: String? = nil
"woohoo".caseInsensitiveEquals(nilString1) // false
Swift 3: You can define your own operator, e.g. ~=.
infix operator ~=
func ~=(lhs: String, rhs: String) -> Bool {
return lhs.caseInsensitiveCompare(rhs) == .orderedSame
}
Which you then can try in a playground
let low = "hej"
let up = "Hej"
func test() {
if low ~= up {
print("same")
} else {
print("not same")
}
}
test() // prints 'same'
You could also make all the letters uppercase (or lowercase) and see if they are the same.
var a = “Cash”
var b = “CASh”
if a.uppercaseString == b.uppercaseString{
//DO SOMETHING
}
This will make both variables as ”CASH” and thus they are equal.
You could also make a String extension
extension String{
func equalsIgnoreCase(string:String) -> Bool{
return self.uppercaseString == string.uppercaseString
}
}
if "Something ELSE".equalsIgnoreCase("something Else"){
print("TRUE")
}
Swift 3
if a.lowercased() == b.lowercased() {
}
Swift 3:
You can also use the localized case insensitive comparison between two strings function and it returns Bool
var a = "cash"
var b = "Cash"
if a.localizedCaseInsensitiveContains(b) {
print("Identical")
} else {
print("Non Identical")
}
extension String
{
func equalIgnoreCase(_ compare:String) -> Bool
{
return self.uppercased() == compare.uppercased()
}
}
sample of use
print("lala".equalIgnoreCase("LALA"))
print("l4la".equalIgnoreCase("LALA"))
print("laLa".equalIgnoreCase("LALA"))
print("LALa".equalIgnoreCase("LALA"))
Related
Declaration:
let listArray = ["kashif"]
let word = "kashif"
then this
contains(listArray, word)
Returns true but if declaration is:
let word = "Kashif"
then it returns false because comparison is case sensitive.
How to make this comparison case insensitive?
Xcode 8 • Swift 3 or later
let list = ["kashif"]
let word = "Kashif"
if list.contains(where: {$0.caseInsensitiveCompare(word) == .orderedSame}) {
print(true) // true
}
alternatively:
if list.contains(where: {$0.compare(word, options: .caseInsensitive) == .orderedSame}) {
print(true) // true
}
if you would like to know the position(s) of the element in the array (it might find more than one element that matches the predicate):
let indices = list.indices.filter { list[$0].caseInsensitiveCompare(word) == .orderedSame }
print(indices) // [0]
You can also use localizedStandardContains method which is case and diacritic insensitive and would match a substring as well:
func localizedStandardContains<T>(_ string: T) -> Bool where T : StringProtocol
Discussion This is the most appropriate method for doing user-level string searches, similar to how searches are done generally in the system. The search is locale-aware, case and diacritic insensitive. The exact list of search options applied may change over time.
let list = ["kashif"]
let word = "Káshif"
if list.contains(where: {$0.localizedStandardContains(word) }) {
print(true) // true
}
you can use
word.lowercaseString
to convert the string to all lowercase characters
For checking if a string exists in a array (case insensitively), please use
listArray.localizedCaseInsensitiveContainsString(word)
where listArray is the name of array
and word is your searched text
This code works in Swift 2.2
Swift 4
Just make everything (queries and results) case insensitive.
for item in listArray {
if item.lowercased().contains(word.lowercased()) {
searchResults.append(item)
}
}
You can add an extension:
Swift 5
extension Array where Element == String {
func containsIgnoringCase(_ element: Element) -> Bool {
contains { $0.caseInsensitiveCompare(element) == .orderedSame }
}
}
and use it like this:
["tEst"].containsIgnoringCase("TeSt") // true
Try this:
let loword = word.lowercaseString
let found = contains(listArray) { $0.lowercaseString == loword }
For checking if a string exists in a array with more Options(caseInsensitive, anchored/search is limited to start)
using Foundation range(of:options:)
let list = ["kashif"]
let word = "Kashif"
if list.contains(where: {$0.range(of: word, options: [.caseInsensitive, .anchored]) != nil}) {
print(true) // true
}
if let index = list.index(where: {$0.range(of: word, options: [.caseInsensitive, .anchored]) != nil}) {
print("Found at index \(index)") // true
}
swift 5, swift 4.2 , use the code in the below.
let list = ["kAshif"]
let word = "Kashif"
if list.contains(where: { $0.caseInsensitiveCompare(word) == .orderedSame }) {
print("contains is true")
}
SWIFT 3.0:
Finding a case insensitive string in a string array is cool and all, but if you don't have an index it can not be cool for certain situations.
Here is my solution:
let stringArray = ["FOO", "bar"]()
if let index = stringArray.index(where: {$0.caseInsensitiveCompare("foo") == .orderedSame}) {
print("STRING \(stringArray[index]) FOUND AT INDEX \(index)")
//prints "STRING FOO FOUND AT INDEX 0"
}
This is better than the other answers b/c you have index of the object in the array, so you can grab the object and do whatever you please :)
Expanding on #Govind Kumawat's answer
The simple comparison for a searchString in a word is:
word.range(of: searchString, options: .caseInsensitive) != nil
As functions:
func containsCaseInsensitive(searchString: String, in string: String) -> Bool {
return string.range(of: searchString, options: .caseInsensitive) != nil
}
func containsCaseInsensitive(searchString: String, in array: [String]) -> Bool {
return array.contains {$0.range(of: searchString, options: .caseInsensitive) != nil}
}
func caseInsensitiveMatches(searchString: String, in array: [String]) -> [String] {
return array.compactMap { string in
return string.range(of: searchString, options: .caseInsensitive) != nil
? string
: nil
}
}
My example
func updateSearchResultsForSearchController(searchController: UISearchController) {
guard let searchText = searchController.searchBar.text else { return }
let countries = Countries.getAllCountries()
filteredCountries = countries.filter() {
return $0.name.containsString(searchText) || $0.name.lowercaseString.containsString(searchText)
}
self.tableView.reloadData()
}
If anyone is looking to search values from within model class, say
struct Country {
var name: String
}
One case do case insensitive checks like below -
let filteredList = countries.filter({ $0.name.range(of: "searchText", options: .caseInsensitive) != nil })
i am currently working on an App that needs to compare three Variables with each other.
Rules for Comparison: The result should only be true if:
All three variables are equal OR All three variables are different
My first idea was something like this, but I hope there is a more elegant solution for this:
if (value1 == value2 && value2 == value3) || (value1 != value2 && value2 != value3 && value3 != value1) {
// True
} else {
// False
}
I would be really happy if someone of you can think of a more elegant solution and share it with me.
Thanks for your help in advance!
If your values are also Hashable you can use a Set. Given the fact that a Set discards duplicate values, you can simplify your check to something like this:
let valuesArray = [value1, value2, value3]
let valuesSet = Set(valuesArray)
if valuesSet.count == 1 || valuesSet.count == valuesArray.count {
// True
} else {
// False
}
For a one-off, that's not too bad.
The more "general" solution is kind of messy, because it needs to track 2 different boolean variables, and handle empty collections correctly.
extension Sequence where Element: Equatable {
func classifyElementEquality() -> (allEqual: Bool, allUnequal: Bool) {
var iterator = self.makeIterator()
guard let first = iterator.next() else {
return (true, true) // all empty
}
return AnyIterator(iterator)
.reduce(into: (allEqual: true, allUnequal: true)) { acc, element in
if first == element {
acc.allUnequal = false
} else {
acc.allEqual = false
}
}
}
}
let (value1, value2, value3) = (1, 2, 3)
let result = [value1, value2, value3].classifyElementEquality()
if result.allEqual || result.allUnequal {
print("They're either all equal, or all unequal")
} else {
print("Some of them are different")
}
It can get a bit simpler if this algorithm targets Collection insteaad of Sequence, because accessing the first element is easier without needing to manually manage an iterator.
extension Collection where Element: Equatable {
func classifyElementEquality() -> (allEqual: Bool, allUnequal: Bool) {
guard let first = self.first else {
return (true, true) // all empty
}
return self
.dropFirst()
.reduce(into: (allEqual: true, allUnequal: true)) { acc, element in
if first == element {
acc.allUnequal = false
} else {
acc.allEqual = false
}
}
}
}
Using this code, I tried to do an insensitive-case search to find companies for a certain major, but I get the error "Expression 'Bool' is ambiguous without more context" at let isFound =.
Why? How do I solve this?
company.majors is a String array. searchValue is a lowercase String
let searchValue = filterOptItem.searchValue?.lowercased()
for company in allCompanies {
//Insensitive case search
let isFound = company.majors.contains({ $0.caseInsensitiveCompare(searchValue) == ComparisonResult.orderedSame })
if (isFound) {
filteredCompanies.insert(company)
}
}
SearchValue is an optional string.
If you are sure that searchValue can't be nil. Please use:
let isFound = company.majors.contains({ $0.caseInsensitiveCompare(searchValue!) == ComparisonResult.orderedSame })
If you are not sure, use:
if let searchValue = filterOptItem.searchValue?.lowercased(){
for company in allCompanies {
//Insensitive case search
let isFound = company.majors.contains({ $0.caseInsensitiveCompare(searchValue) == ComparisonResult.orderedSame })
if (isFound) {
filteredCompanies.insert(company)
}
}
}
Depends very much of a context and possible extensions, however cod itself seems wrong: you should either implicit closure let isFound = company.majors.contains{ $0.caseInsensitiveCompare(searchValue) == ComparisonResult.orderedSame } or use parameter name where:.
Fixed code work fine for Swift4 playgrounds:
let company = (name: "Name", majors: ["a", "b", "cas"])
let searchValue = "a"
let isFound = company.majors.contains{ $0.caseInsensitiveCompare(searchValue) == ComparisonResult.orderedSame }
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
}
else
{
//Record Not found
}
Is there any pretty way to test the below? I have multiple parameters which I need to know if any one of them is nil
This is what I am using now, I am sure there is an efficient way to test all and type nil once but not sure how:
if title == nil || name == nil || height == nil || productName == nil {
//Do something
}
I am using ObjectMapper and at they moment, they don't support error handling, hence, my init() throws errors and I need to check if the values from Map are nil or not and through if they are.
I have created a simple extension on CollectionType to check for a collection of Optional value, if at least one element is not nil, if all elements have value or if none have value.
extension CollectionType where Generator.Element == Optional<AnyObject>, Index.Distance == Int {
func allNotNil() -> Bool {
return !allNil()
}
func atleastOneNotNil() -> Bool {
return self.flatMap { $0 }.count > 0
}
func allNil() -> Bool {
return self.flatMap { $0 }.count == 0
}
}
var title: String? = ""
var name: String? = ""
var height: Float? = 1
var productName: String? = ""
[title, name, height, productName].allNotNil()
[title, name, height, productName].atleastOneNotNil()
[title, name, height, productName].allNil()
In your case, you could use it like this,
if [title, name, height, productName].atLeastOneNotNil() {
}
Or, you could discard the extension above and simply use it like this,
if [title, name, height, productName].flatMap { $0 }.count > 0 {
}
For Swift 4,
extension Collection where Element == Optional<Any> {
func allNotNil() -> Bool {
return !allNil()
}
func atleastOneNotNil() -> Bool {
return self.flatMap { $0 }.count > 0
}
func allNil() -> Bool {
return self.flatMap { $0 }.count == 0
}
}
Updates for Swift 5,
Few new functions have been added to CollectionType such as first(where:) and allSatisfy(where:) and it is used here.
extension Collection where Element == Optional {
func allNil() -> Bool {
return allSatisfy { $0 == nil }
}
func anyNil() -> Bool {
return first { $0 == nil } != nil
}
func allNotNil() -> Bool {
return !allNil()
}
}
Here's a short version using a collection literal:
let isAnyNil = ([title, name, height, productName, nil] as [Optional<Any>]).contains { $0 == nil }
It's similar to #GeneratorOfOne's flatMap and count variant. I prefer the simplicity of contains.
If you do this often, I'd go with a free function to avoid the need to specify the type:
func isAnyNil(optionals: Optional<Any> ...) -> Bool {
return optionals.contains { $0 == nil }
}
isAnyNil(title, name, height, productName)
I'm not sure why you need to know, but if it is kind of unwrapping than it better to do so in Swift 2.0
if let email = emailField?.text, password = passwordField?.text {
//here you have both email & password
}
if you enter a method and need to do something in case any of them is nil, I would recommend using a guard:
guard let email = emailField?.text else {
// It is nil, do something
return
}
// if we got here, we have 'email' and it is not nil.
Side Note:
I'm guessing when you mean efficient you really talk about pretty or easy and not really efficient, because in either cases you would have to evaluate all arguments to see if they are nil.
If indeed you just want it to be pretty, you could use .filter to check
var nilElements = [email,password].filter{0 == nil}
you will get back only the elements which are nil
Is it possible to count all properties who are not nil?
For example:
class test {
var val1:Int?
var val2:Int?
var val3:Int?
var val4:Int?
var val5:Int?
}
var test = test()
test.val1 = 1
test.val2 = 2
How to find out that 2 properties are set? I could check for each one with (!= nil) - but is there an easier (and better) way?
You can do this manually, with a convenience method:
func numberOfNonNil() -> Int {
let vals = [val1, val2, val3, val4, val5]
return flatMap { $0 }.count
}
flatMap(_:) takes a closure that takes a single element of the array and returns an optional value (Element -> T?), and returns the result of applying that closure to each element of the array, with nil values ignored.
The only way to make this simpler would be to store your values as an array of optional Ints in the first place:
class Test {
var vals: [Int?]
}
You can then still access each individual value using the array subscript notation (let val2 = vals[1]). You could then just use the second line in the convenience method above (filter then count) to get the number of non-nil values:
let nonNilCount = vals.flatMap { $0 }.count
If your values are of different types, this approach will still work if you cast the array to a type that encompasses all the different types:
class Test {
var val1: Int?
var val2: Double
var val3: String
var val4: MyRandomClass?
func numberOfNonNil() -> Int {
let vals = [val1, val2, val3, val4, val5] as [Any?]
return flatMap { $0 }.count
}
}
This works because all the values can be expressed as the type Any?.
I don't think there's a way to do this, but you can implement your own function like this:
class test {
var val1:Int?
var val2:Int?
var val3:Int?
var val4:Int?
var val5:Int?
func setVarsCount() -> Int {
var setVariablesCount = 0
if val1 != nil {
setVariablesCount++
}
if val2 != nil {
setVariablesCount++
}
if val3 != nil {
setVariablesCount++
}
if val4 != nil {
setVariablesCount++
}
if val5 != nil {
setVariablesCount++
}
return setVariablesCount
}
}
#Stuarts answer is good, but you have to know the properties of the class, and if you add another property to the class, you also have to modify your method. To avoid this problem, you can use reflection, like
Swift 1.2:
func numberOfOptionalProperties() -> Int {
let mirror = reflect(self)
var numberOfOptionalProperties = 0
for index in 0..<mirror.count {
if mirror[index].1.disposition == .Optional {
++numberOfOptionalProperties
}
}
return numberOfOptionalProperties
}
Swift 2.0:
func numberOfOptionalProperties() -> Int {
return Mirror(reflecting: self).children.reduce(0) {
Mirror(reflecting: $1.value).displayStyle == .Optional ? $0 + 1 : $0
}
}