Are arrays equatable? Can I compare them using ==
Prior to Swift 4.1, Array
didn't conform Equatable
. There was however an overload of ==
that compared two arrays with Equatable
elements, which is what enabled this to compile:
if ["1", "2"] == ["1", "2"] { // using <T : Equatable>(lhs: [T], rhs: [T]) -> Bool
print("true")
}
However in Swift 4.1 (available with Xcode 9.3), Array<Element>
now conforms to Equatable
when its Element
conforms to Equatable
. This change is given in the changelog:
Swift 4.1
[...]
- SE-0143 The standard library types
Optional
, Array
, ArraySlice
, ContiguousArray
, and Dictionary
now conform to the Equatable
protocol when their element types conform to Equatable
. This allows the ==
operator to compose (e.g., one can compare two values of type [Int : [Int?]?]
with ==
), as well as use various algorithms defined for Equatable
element types, such as index(of:)
.
Your example with multiDimArr.removeDups()
compiles and runs as expected in 4.1, yielding the result [[1, 2, 3], [1, 2, 4]]
.
In Swift 4.0.3, you could hack it by adding another overload of removeDups()
for nested arrays:
extension Array {
func removeDups<T : Equatable>() -> [Element] where Element == [T] {
var result = [Element]()
for element in self{
if !result.contains(where: { element == $0 }) {
result.append(element)
}
}
return result
}
}
let multiDimArr = [[1, 2, 3], [1, 2, 3], [1, 2, 4]]
print(multiDimArr.removeDups()) // [[1, 2, 3], [1, 2, 4]]
This does unfortunately lead to some code duplication, but at least you'll be able to get rid of it when updating to 4.1.
The fact that this example doesn't compile in either 4.0.3 or 4.1:
if [1, 2] == [1, 2] { // error: Ambiguous use of operator '=='
print("true")
}
is due to the bug SR-5944 – the compiler is considering it to be ambiguous due to ==
overloads for IndexSet
and IndexPath
(both of which are ExpressibleByArrayLiteral
). But Swift should default an array literal to Array
though, resolving the ambiguity.
Saying either:
if [1, 2] as [Int] == [1, 2] {
print("true")
}
or not importing Foundation
resolves the issue.
Finally, it's worth noting that the performance of removeDups()
can be improved if the Element
type is also Hashable
, allowing it to run in linear, rather than quadratic time:
extension Array where Element : Hashable {
func removeDups() -> [Element] {
var uniquedElements = Set<Element>()
return filter { uniquedElements.insert($0).inserted }
}
}
Here we're using a set to store the elements that we've seen, omitting any that we've already inserted into it. This also allows us to use filter(_:)
, as @Alexander points out.
And in Swift 4.2, Array
also conditionally conforms to Hashable
when its Element
is Hashable
:
Swift 4.2
[...]
- SE-0143 The standard library types
Optional
, Array
, ArraySlice
, ContiguousArray
, Dictionary
, DictionaryLiteral
, Range
, and ClosedRange
now conform to the Hashable
protocol when their element or bound types (as the case may be) conform to Hashable
. This makes synthesized Hashable
implementations available for types that include stored properties of these types.
[Int]
beingEquatable
. – SunderanceArray<Element>
conforms toEquatable
whenElement
conforms toEquatable
, somultiDimArr.removeDups()
will compile in 4.1 provided you relax your (currently over-constrained) extension towhere Element : Equatable
. – Quatrain[1, 2] == [1, 2]
doesn't compile is the bug SR-5944, the compiler is considering it to be ambiguous due to==
overloads forIndexSet
andIndexPath
(both of which areExpressibleByArrayLiteral
). But Swift should default an array literal toArray
though, resolving the ambiguity. Saying[1, 2] as [Int] == [1, 2]
or not importingFoundation
resolves the issue. – Quatrain4.0.2
. About your 1st comment. provided you relax your (currently over-constrained. I actually intended it to beEquatable
. (I'll edit it now). There is no reason to have it be forComparable
. And is this any different for Swift 3? – Privilege[1, 2] == [1, 2]
used to compile in Swift 3, but I haven't verified that for myself. – Quatrain[[1,2,3], [1,2,3], [1,2 ,4]]
is not a multidimensional array – it's a nested array (true multidimensional arrays enforce that the dimensions are of consistent sizes). – Quatraincontains
against anArray
like that leads this to haveO(N^2)
performance :( – Doradorado[Int]
not hashable...meaning you can't do something likelet removedDups = Array(Set(originalArray))
. Is there a better way? – Privilege[Int]
becomes hashable – Doradorado