Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift equality operator on nested arrays

Tags:

swift

Why does the last statement fail to compile with the error: Binary operator '==' cannot be applied to two '[[Simple]]’ operands, and is there a way way to modify the Simple struct or extend the == operator to be able to perform equality checks on nested arrays (or dictionaries)?

var i1: [Int] = [1]
var i2: [Int] = [1]
i1 == i2 // -> true


var i3: [[Int]] = [[1], [2]]
var i4: [[Int]] = [[1], [2]]
i3 == i4 // -> true


struct Simple: Equatable, Hashable {
    let message: String

    var hashValue: Int {
        return message.hashValue
    }
}
func ==(lhs: Simple, rhs: Simple) -> Bool {
    return lhs.message == rhs.message
}

var a: [Simple] = [Simple(message: "a")]
var b: [Simple] = [Simple(message: "a")]
a == b // -> true

var x: [[Simple]] = [[Simple(message: "a")], [Simple(message: "b")]]
var y: [[Simple]] = [[Simple(message: "a")], [Simple(message: "b")]]
x == y // -> ERROR! Binary operator '==' cannot be applied to two '[[Simple]]’ operands
like image 223
Joe Avatar asked Oct 27 '15 20:10

Joe


2 Answers

Update: Conditional conformance has been implemented in Swift 4.1. In particular:

The standard library types Optional, Array, and Dictionary now conform to the Equatable protocol when their element types conform to Equatable. ...

(from the Swift CHANGELOG).

Arbitrarily nested arrays of Equatable elements are Equatable now and can be compared with ==. Your code

var x: [[Simple]] = [[Simple(message: "a")], [Simple(message: "b")]]
var y: [[Simple]] = [[Simple(message: "a")], [Simple(message: "b")]]
x == y

compiles in Xcode 9.3 if Simple is Equatable.


(Old answer:) The reason is similar as in Why is Equatable not defined for optional arrays. Arrays can be compared with == if the element type is Equatable:

/// Returns true if these arrays contain the same elements.
public func ==<Element : Equatable>(lhs: [Element], rhs: [Element]) -> Bool

That's why

var a: [Simple] = [Simple(message: "a")]
var b: [Simple] = [Simple(message: "a")]
a == b // -> true

compiles.

But even for equatable types T, Array<T> does not conform to the Equatable protocol, compare Why can't I make Array conform to Equatable?. Therefore, in

var x: [[Simple]] = [[Simple(message: "a")], [Simple(message: "b")]]
var y: [[Simple]] = [[Simple(message: "a")], [Simple(message: "b")]]
x == y // -> ERROR! Binary operator '==' cannot be applied to two '[[Simple]]’ operands

x and y are arrays with the element type [Simple] which does not conform to the Equatable protocol, and there is no matching == operator.

You could define a generic == operator for simply nested arrays as

func ==<Element : Equatable> (lhs: [[Element]], rhs: [[Element]]) -> Bool {
    return lhs.count == rhs.count && !zip(lhs, rhs).contains {$0 != $1 }
}

or more simply (as suggested by @kennytm):

func ==<Element : Equatable> (lhs: [[Element]], rhs: [[Element]]) -> Bool {
    return lhs.elementsEqual(rhs, by: ==)
}

This makes x == y compile and work as expected. At present, there seems to be no way to define a == operator on arbitrarily nested arrays.

like image 143
Martin R Avatar answered Oct 03 '22 05:10

Martin R


You can do it by implementing a == function for it, like following:

func == (lhs: [[Simple]], rhs: [[Simple]]) -> Bool {
    //your code
}
like image 22
Adam Avatar answered Oct 03 '22 06:10

Adam