Problem:
Testing arrays (with the same type of Equatable element type) for equivalence works as expected:
// This works:
let a = [1, 2, 3]
let b = [1, 2, 3]
print(a == b) // true
While the same is not true for nested arrays (eg arrays of arrays with the same type of Equatable element type):
// This will not compile:
let a = [[1, 2, 3], [10, 20, 30]]
let b = [[1, 2, 3], [10, 20, 30]]
print(a == b) // Compile Error: Binary operator '==' cannot be applied to two [Array<Int>] operands.
EDIT: Actually, as it turned out below, this will already work but only if we import Foundation / Cocoa ... But it's unclear how/why. Can someone explain?
But for now, let's assume we are in a pure Swift context, so no imports.
You could of course do eg this:
print(a.elementsEqual(b) { $0.0.elementsEqual($0.1) }) // true
Or write a == operator for arrays of arrays with equatable elements.
But these solutions will only work for the special case of exactly one level of nestedness.
My current solution:
Below is the least ugly general solution I can come up with (in Swift 2).
It would be much better if we could make arrays with equatable element types conform to Equatable, but AFAICS that is not possible. Here's a thread about that:
https://forums.developer.apple.com/thread/7172
The main disadvantage of the solution below is that we have to state explicitly that an array is equatable (through the eq computed property which returns a custom EquatableSequence) and then if we want to get back the array we will have to do that explicitly as well (through the ar computed property).
// ---- Solution ----
extension SequenceType where Generator.Element : Equatable {
var eq: EquatableSequence<Self> { return EquatableSequence(self) }
var ar: Array<Generator.Element> { return Array<Generator.Element>(self) }
}
protocol EquatableSequenceType: SequenceType, Equatable { }
struct EquatableSequence<S : SequenceType where S.Generator.Element : Equatable>: EquatableSequenceType {
let base: S
init(_ base: S) { self.base = base }
func generate() -> S.Generator { return base.generate() }
func underestimateCount() -> Int { return base.underestimateCount() }
}
func ==<T: EquatableSequenceType where T.Generator.Element: Equatable>(lhs: T, rhs: T) -> Bool {
return lhs.elementsEqual(rhs)
}
// ---- Demo ----
// Array of arrays turned into equatable sequences:
let x = [[1, 2].eq, [1, 2, 3].eq]
let y = [[1, 3].eq, [1, 2, 3].eq]
print(x == y) // true
// Arrays turned into equatable sequences:
let a = [1, 2, 3].eq
let b = [1, 2, 3].eq
let c = [3, 2, 1].eq
// Nested equatable sequences:
let aa = [a, a].eq
let ab = [a, b].eq
let ac = [a, c].eq
// Nested nested equatable sequences:
let aaaa = [aa, aa].eq
let aaab = [aa, ab].eq
let aaac = [aa, ac].eq
print(a == b) // true
print(a == c) // false
print(aa == ab) // true
print(aa == ac) // false
print(aaaa == aaab) // true
print(aaaa == aaac) // false
print(aaaa.ar[0].ar[0].ar[0]) // Solution is not very nice when you want to get back to a
// nested array again, could of course write an EquatableArray
// analogous to EquatableSequence but that would mean a lot of
// forwarding methods.
Has anyone arrived at a less hackish general solution for testing nested arrays for equivalence?