Reputation: 4485
I'm trying to add an extension method in Array like so:
extension Array {
func contains(obj: T) -> Bool {
let filtered = self.filter {$0 == obj}
return filtered.count > 0
}
}
But self.filter {$0 == obj}
don't work. Compiler error:
could not find an overload for '==' that accepts the supplied arguments
Upvotes: 4
Views: 13635
Reputation: 384
This works with Swift 2.1 for reference types pretty good.
extension SequenceType where Generator.Element: AnyObject {
func contains(obj: Self.Generator.Element?) -> Bool {
if obj != nil {
for item in self {
if item === obj {
return true
}
}
}
return false
}
}
For value types you can add this:
extension SequenceType where Generator.Element: Equatable {
func contains(val: Self.Generator.Element?) -> Bool {
if val != nil {
for item in self {
if item == val {
return true
}
}
}
return false
}
}
Upvotes: 1
Reputation: 15422
As I mentioned in the comments, there is a contains
function. But to answer the question of how to write an extension and what the compiler error means:
The elements in the array can't necessarily be compared with ==
. You need to make sure the parameter is Equatable
and you need to make sure the array element is of the same type.
extension Array {
func contains<T : Equatable>(obj: T) -> Bool {
let filtered = self.filter {$0 as? T == obj}
return filtered.count > 0
}
}
Swift 2 includes SequenceType.contains
, which is exactly what you were trying to create.
This is made possible by a Swift syntax that allows restricting methods to certain (e.g. Equatable) type arguments. It looks like this:
extension SequenceType where Generator.Element: Equatable {
func contains(element: Self.Generator.Element) -> Bool {
...
}
}
Upvotes: 13
Reputation: 143
I found that the built-in contains doesn't work with reference types. I needed this and solved it with the code below. I'm pasting it here because somebody else might be confused about contains() like I was.
extension Array {
func containsReference(obj: AnyObject) -> Bool {
for ownedItem in self {
if let ownedObject: AnyObject = ownedItem as? AnyObject {
if (ownedObject === obj) {
return true
}
}
}
return false
}
}
Upvotes: 1
Reputation: 17762
Not perfect, but this version built on nschum's answer supports optional arguments (though not arrays with optional types) as well:
extension Array {
private func typeIsOptional() -> Bool {
return reflect(self[0]).disposition == .Optional
}
func contains<U : Equatable>(obj: U) -> Bool {
if isEmpty {
return false
}
if (typeIsOptional()) {
NSException(name:"Not supported", reason: "Optional Array types not supported", userInfo: nil).raise()
}
// cast type of array to type of argument to make it equatable
for item in self.map({ $0 as? U }) {
if item == obj {
return true
}
}
return false
}
// without this version, contains("foo" as String?) won't compile
func contains<U : Equatable>(obj: U?) -> Bool {
if isEmpty {
return false
}
if (typeIsOptional()) {
NSException(name:"Not supported", reason: "Optional Array types not supported", userInfo: nil).raise()
}
return obj != nil && contains(obj!)
}
}
If you have an array of optionals, you can get a copy of it with non-optionals (nil arguments removed) with this global function thanks to jtbandes:
func unwrapOptionals<T>(a: [T?]) -> [T] {
return a.filter { $0 != nil }.map { $0! }
}
Usage:
1> func unwrapOptionals<T>(a: [T?]) -> [T] {
2. return a.filter { $0 != nil }.map { $0! }
3. }
4>
5> let foo = ["foo" as String?]
foo: [String?] = 1 value {
[0] = "foo"
}
6> let bar = unwrapOptionals(foo)
bar: [String] = 1 value {
[0] = "foo"
}
For good measure, add one that just returns the array if its type is not optional. This way you avoid runtime errors if you call unwrapOptionals()
on a non-optional array:
func unwrapOptionals<T>(a: [T]) -> [T] {
return a
}
Note you might think you could just call unwrapOptionals
inside func contains<U : Equatable>(obj: U?)
. However, that doesn't work, because the Element
type in the Array extension is just a type--it doesn't "know" it's an optional type. So if you call unwrapOptionals
, the second version will be invoked, and you'll just get the array full of optionals back.
Upvotes: 0
Reputation: 1338
you don't actually need to write an extension, you can use the global func contains
from the Swift library:
contains([1,2,3], 1)
Upvotes: 32