Basic == overloading fails - ios

My desired end result is to be able to compare to [Server] objects for equality, like so:
let server1 = Server(username: "1")
let server2 = Server(username: "2")
let server1Array = [server1]
let server2Array = [server2]
print(server1Array == server2Array)
I have a class called Server, and I want to overload the == for arrays of Server:
// Server.swift
import Foundation
infix operator ==: AssignmentPrecedence
class Server: ServerRepresentable {
let username: String
init(username: String) {
self.username = username
}
}
// This protocol's only purpose is to allow me to use the `where` clause in the extension below
protocol ServerRepresentable {
var username: String { get }
}
extension Array where Element: ServerRepresentable {
static func == (lhs: Array<ServerRepresentable>, rhs: Array<ServerRepresentable>) {
let server1Usernames = lhs.map { $0.username }
let server2Usernames = rhs.map { $0.username }
// ERROR: Cannot convert value of type '[String]' to expected argument type 'Bool'
return server1Usernames == server2Usernames
}
}
As you can see, I get the error "Cannot convert value of type '[String]' to expected argument type 'Bool'" when I try to perform server1Usernames == server2Usernames, which are both arrays of strings. This is strange because the == operator should already come overloaded in [String] comparisons.
What gives? Is the compiler getting confused because I'm overloading the operator in this class?

There is no need to extend Array. Just make sure your Server conforms to Equatable protocol:
class Server: ServerRepresentable, Equatable {
let username: String
init(username: String) {
self.username = username
}
static func == (lhs: Server, rhs: Server) -> Bool {
lhs.username == rhs.username
}
}
let server1 = Server(username: "1")
let server2 = Server(username: "2")
let server1Array = [server1]
let server2Array = [server2]
print(server1Array == server2Array) // "false\n"
Regarding your question you forgot to add a returning type Bool but the error displayed by the code you have posted should be Unexpected non-void return value in void function

This only works for string, int, double, float arrays.
You could try something like this
To check if there is a difference
for server1 in servers1 {
var name1 = [String]()
var name2 = String()
for server2 in servers2 {name1.append(server2.name)}
name2 = server1.name
if name1.contains(name2) {
print("found duplication")
}
To find the differences
for server1 in servers1 {
var name1 = [String]()
var name2 = String()
for server2 in servers2 {name1.append(server2.name)}
name2 = server1.name
print(name1. difference(name2))
}
//
extension Array where Element: Hashable {
func difference(from other: [Element]) -> [Element] {
let thisSet = Set(self)
let otherSet = Set(other)
return Array(thisSet.symmetricDifference(otherSet))
}
}

Related

Swift: Cannot convert value of type '()' to specified type on init

I have Struct init and I'm trying to filter the an array in init:
public struct doSomething: Codable {
public var listOfStuff: [String]
init(someStuff: [String]) {
var clone = someStuff
let stuff: [String] = clone.removeAll { $0 == "myName"}
listOfStuff = stuff
}
}
On this line let stuff: [String] = clone.removeAll { $0 == "myName"} I'm getting this error:
error: cannot convert value of type '()' to specified type '[String]
Any of you knows why I'm getting this error or if you know a work around?
I'll really appreciate your help.
removeAll does not return a value. That's the same thing as returning Void, which is the same thing as ().
clone.removeAll { $0 == "myName" }
listOfStuff = clone
Better yet, don't even use it.
init(someStuff: [String]) {
listOfStuff = someStuff.filter { $0 != "myName" }
}

Array comparison on Swift not working as expected

I've got a class Email:
import SwiftyJSON
class Email: NSObject {
required init?(JSON jsonObject: AnyObject) {
let emailJsonObject = JSON(jsonObject)
self.email = emailJsonObject["emailaddress"].stringValue
self.emailType = emailJsonObject["emailtype"].stringValue
}
var email: String
var emailType: String
}
func == (lhs: Email, rhs: Email) -> Bool {
return lhs.email == rhs.email && lhs.emailType == rhs.emailType
}
Now if I have two Arrays:
let newEmails = emailsJsonObjects.map({ return Email(JSON: $0)! }).sort({ $0.0.email < $0.1.email })
let currentEmails = (self.emails as! [Email]).sort({ $0.0.email < $0.1.email })
Both have one element, and they have the same email and emailType, here's what I've got by comparing them:
(lldb) po newEmails.count
1
(lldb) po currentEmails.count
1
(lldb) po newEmails == currentEmails
false
(lldb) po newEmails[0] == currentEmails[0]
true
Am I missing something? Are the comparisons different?
The problem is that, because your objects inherit from NSObject the array is using isEqual() to do the comparison of array elements. Add the following override to your class definition
override func isEqual(object: AnyObject?) -> Bool
{
if let rhs = object as? Email
{
return self.email == rhs.email && self.emailType == rhs.emailType
}
return false
}
And it should all be fine.

Filter array of classes in Swift

I am trying to filter an array containing classes so that only the class that is found in another array will be added to an array. This is what I have so far:
class Match : Equatable {
var name: String
var value: String
init(name: String, value: String) {
self.name = name
self.value = value
}
func ==(lhs: Match, rhs: Match) -> Bool {
return lhs.name == rhs.name && lhs.value == rhs.value
}
// attempt to filter array containing Match structs
let terms = [Match]()
let someOtherObjects = [Match]()
let sampleMatch = Match(name: "someName", value: "someValue")
someOtherObjects.append(sampleMatch)
filteredTerms = terms.filter { term in
if attemptedCombos.contains(sampleMatch) {
return true
}
}
However the compiler does not let me build with the error:
"Cannot convert value of type 'Match' to expected argument type
'#noescape (Match) throws -> Bool'
Any ideas?
Updated using Set (as it seems as if you want the intersection of two [Match] arrays). In addition to Equatable, you must let your Match class conform to Hashable for instances of it to be allowed as elements in a Set.
class Match : Equatable, Hashable {
var name: String
var value: String
init(_ name: String, _ value: String) {
self.name = name
self.value = value
}
var hashValue: Int {
get {
return name.hashValue << 20 + value.hashValue
}
}
}
func ==(lhs: Match, rhs: Match) -> Bool {
return lhs.name == rhs.name && lhs.value == rhs.value
}
Example:
/* Example */
var mySetA : Set<Match> = [Match("foo", "bar"), Match("foo", "foo"), Match("barbar", "foo")]
var mySetB = Set<Match>()
mySetB.insert(Match("barbar", "bar"))
mySetB.insert(Match("bar", "foo"))
mySetB.insert(Match("foo", "bar"))
mySetB.insert(Match("foo", "foo"))
let myIntersect = mySetA.intersect(mySetB)
for match in myIntersect {
print("name: " + match.name + ", value: " + match.value)
}
/* name: foo, value: foo
name: foo, value: bar */
After chatting with the OP we've solved the issue in chat. I'm not sure what the convention are here, but I'll summarize the additional information given by the OP in chat, and the solution to the problem. Consider the block above as a solution to the question above, and the block below as a quite narrow solution to the question above complemented with more specifics from the OP.
The "filter" array of objects are of a different class type (Tern) than the array to be filtered (Match), where these two classes share some class properties.
A natural enquiry to the OP was if it was acceptable to let both these classes have one common superclass; it was.
In addition to the above, one of the properties common for both classes were of a custom enum type, posted in chat by the author.
The final solution used, as above, Set and .intersect():
/* custom enum given by OP in chat */
enum Declension : String {
case firstDeclensionFem = "a:a:am:ae:ae:a:ae:ae:as:arum:is:is"
case secondDeclensionMasc = "us:er:um:i:o:o:i:i:os:orum:is:is"
case secondDeclensionNeu = "um:um:um:i:o:o:a:a:a:orum:is:is"
case thirdDeclensionMasc = " : :em:is:i:e:es:es:es:um:ibus:ibus"
case thirdDeclensionMascSpecial = " : :em:is:i:e:es:es:es:ium:ibus:ibus"
case fourthFem = "us:us:um:us:ui:u:us:us:us:uum:ibus:ibus"
case fourthNeu = "u:u:u:us:u:u:ua:ua:ua:uum:ibus:ibus"
case fifthMasc = "es:es:em:ei:ei:e:es:es:es:erum:ebus:ebus"
case unknown
static let allValues = [firstDeclensionFem, secondDeclensionMasc, secondDeclensionNeu, thirdDeclensionMasc, thirdDeclensionMascSpecial, fourthFem, fourthNeu, fifthMasc]
}
/* use a superclass and let the sets below have members that
are declared to be of this superclass type */
class MyMatchTypes : Equatable, Hashable {
var latin: String
var declension: Declension
init(_ latin: String, _ declension: Declension) {
self.latin = latin
self.declension = declension
}
var hashValue: Int {
get {
return latin.hashValue << 20 + declension.hashValue
}
}
}
func ==(lhs: MyMatchTypes, rhs: MyMatchTypes) -> Bool {
return lhs.latin == rhs.latin && lhs.declension == rhs.declension
}
/* the two classes mentioned in chat: use as subclasses */
class Term : MyMatchTypes {
var meaning: String
var notes: String
var genStem: String
init(_ latin: String, _ declension: Declension, _ meaning: String, _ genStem: String, _ notes: String) {
self.meaning = meaning
self.notes = notes
self.genStem = genStem
super.init(latin, declension)
}
}
class Match : MyMatchTypes {
// ... add stuff
// super init is OK
}
/* Example */
/* ----------------------------------------------- */
/* Set of `Match` objects */
var mySetA = Set<MyMatchTypes>()
mySetA.insert(Match("foo", Declension.firstDeclensionFem))
mySetA.insert(Match("bar", Declension.fourthFem))
mySetA.insert(Match("foofoo", Declension.fourthFem))
mySetA.insert(Match("barbar", Declension.fifthMasc))
/* Set of `Term` objects */
var mySetB = Set<MyMatchTypes>()
mySetB.insert(Term("fooshy", Declension.fourthFem, "a", "b", "c"))
mySetB.insert(Term("barbar", Declension.fifthMasc, "a", "b", "c"))
mySetB.insert(Term("bar", Declension.fourthFem, "a", "b", "c"))
mySetB.insert(Term("foofoo", Declension.firstDeclensionFem, "a", "b", "c"))
mySetB.insert(Term("foobar", Declension.fourthFem, "a", "b", "c"))
/* compute intersection */
let myIntersect = mySetA.intersect(mySetB)
for obj in myIntersect {
print("latin: " + obj.latin + ", declension: \(obj.declension)")
}
/* latin: barbar, declension: fifthMasc
latin: bar, declension: fourthFem */
If Match confirms to Equatable protocol your code should work. Following code compiles successfully:
class Match : Equatable {
var name: String
var value: String
init(name: String, value: String) {
self.name = name
self.value = value
}
}
func ==(lhs: Match, rhs: Match) -> Bool {
return lhs.name == rhs.name && lhs.value == rhs.value
}
let sampleMatch1 = Match(name: "someNameA", value: "someValue")
let sampleMatch2 = Match(name: "someNameA", value: "someValue")
let sampleMatch3 = Match(name: "someNameB", value: "someValue")
let sampleMatch4 = Match(name: "someNameC", value: "someValue")
let terms = [sampleMatch1, sampleMatch3]
var someOtherObjects = [sampleMatch2, sampleMatch4]
let filteredTerms = terms.filter { term in
return someOtherObjects.contains(term)
}
Result is:

Swift 2.0 Tuple pattern element label must be '_'

I have been trying to fix all my code since swift 2.0 update. I have a problem that seems to be the way tuples work now:
public func generate() -> AnyGenerator <(String, JSON)> {
switch self.type {
case .Array:
let array_ = object as! [AnyObject]
var generate_ = array_.generate()
var index_: Int = 0
return anyGenerator{
if let element_: AnyObject = generate_.next() {
return ("\(index_++)", JSON(element_))
} else {
return nil
}
}
case .Dictionary:
let dictionary_ = object as! [String : AnyObject]
var generate_ = dictionary_.generate()
return anyGenerator{
if let (key_: String, value_: AnyObject) = generate_.next() {
return (key_, JSON(value_))
} else {
return nil
}
}
default:
return anyGenerator{
return nil
}
}
}
Specifically the line:
if let (key_: String, value_: AnyObject) = generate_.next()
Is throwing the error: Tuple pattern element label 'key' must be '_'
I tried to make that change already, but I didnt work...
Any ideas?
The problem is: We cannot use type annotation inside of tuple patterns anymore.
In the release notes:
Type annotations are no longer allowed in patterns and are considered part of the outlying declaration. This means that code previously written as:
var (a : Int, b : Float) = foo()
needs to be written as:
var (a,b) : (Int, Float) = foo()
if an explicit type annotation is needed. The former syntax was ambiguous with tuple element labels. (20167393)
So, you can:
if let (key_, value_): (String, AnyObject) = generate_.next() {
But in this case, you could omit : (String, AnyObject):
if let (key_, value_) = generate_.next() {

How to compare two strings ignoring case in Swift language?

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"))

Resources