Jay Hu
Jay Hu

Reputation: 6091

How can I make a extension for array of specific type in Swift

for say I have:

struct S {
    var num = 0
}

I wanna implement a function allEqual() as extension for Array<S>, so I could do thing like

var s1 = S()
var s2 = S()
var s3 = S()
var equality = [s1,s2,s3].allEqual()

Upvotes: 8

Views: 2543

Answers (3)

J.Wang
J.Wang

Reputation: 1236

In the latest Swift 3.1. You can do same-type constraints on the concrete type extension. So in your case, you can do:

extension Array where Element == S {
    func allEqual() -> Bool {
        ...
    }
}

Upvotes: 3

sharpBaga
sharpBaga

Reputation: 151

for say the specific type is S

extension CollectionType where Generator.Element == S {
}

CollectionType Protocol

Upvotes: 6

Kametrixom
Kametrixom

Reputation: 14973

I created an extension that works lazily for any SequenceType whose Elements are Equatable. In Swift, it is good practice to make your code work on exactly what it can work on: To be able to get whether all elements are equal it has to be a sequence of equatable values. Here is the code:

extension SequenceType where Generator.Element : Equatable {
    var allEqual : Bool {
        var gen = generate()           // Generate the sequence for the first element
        return gen.next().map { fst in // Get first element and map it (when existing)
            !contains { fst != $0 }    // To whether self doesn't contain any elements unequal to first
        } ?? true                      // When first is nil, return true
    }                                   
}

Or you can also do it more iterative (which is basically the same I think is faster?):

extension SequenceType where Generator.Element : Equatable {
    var allEqual : Bool {
        var gen = generate()
        let first = gen.next()
        return !contains { $0 != first }
    }
}

Also you should make your struct conform to the Equatable protocol like this:

func ==(lhs: S, rhs: S) -> Bool {
    return lhs.x == rhs.x
}

because every value type should be equatable and obviously it makes total sense in your code to do this.

Here is some test code:

[S(x: 3), S(x: 3), S(x: 3)].allEqual    // true
[S(x: 5), S(x: 3), S(x: 3)].allEqual    // false
[S(x: 5), S(x: 5)].allEqual             // true
[S(x: 0)].allEqual                      // true
[S]().allEqual                          // true

Note that it is lazy, which means that it will return false as soon as there is an element which isn't equal to the first one, so if you have something like this:

let longList = [S(x: 5)] + [S](count: 100000, repeatedValue: S(x: 4))
    // [{x 5}, {x 4}, {x 4}, {x 4}, {x 4}, {x 4}, {x 4}, ...
longList.allEqual   // false

will return at the second element, because there is an equality already

EDIT: My previous function was unnecessarily complicated. The new one is still lazy just shorter

Upvotes: 1

Related Questions