Reputation: 6361
It's simple to extend a Collection type in swift to have a custom function for particular Element types:
struct MyStruct {
let string: String
}
extension Set where Element == MyStruct {
func getFirstWith(string: String) -> Element? {
return filter({ $0.string == string }).first
}
}
But suppose your Element type is generic?
protocol MyProtocol {}
struct MyGenericStruct<T: MyProtocol> {
let string: String
}
// error on next line:
// error: expected '>' to complete generic argument list
// extension Set where Element == MyGenericStruct<T: MyProtocol> {
// ^
extension Set where Element == MyGenericStruct<T: MyProtocol> {
func getFirstWith(string: String) -> Element? {
return filter({ $0.string == string }).first
}
}
// error on next line:
// error: use of undeclared type 'T'
// extension Set where Element == MyGenericStruct<T> {
// ^
extension Set where Element == MyGenericStruct<T> {
func getFirstWith(string: String) -> Element? {
return filter({ $0.string == string }).first
}
}
It's unclear to me how to declare that my element is a generic.
Upvotes: 3
Views: 1970
Reputation: 1734
If I do understand the purpose, you probably need to have more implementation in your protocol
and structure
.
First, element's protocol has to be hashable and equitable.
protocol MyProtocol: Hashable, Equatable {
var string: String { get }
}
Therefore, MyGenericStruct
will look like the following.
struct MyGenericStruct: MyProtocol {
let string: String
var hashValue: Int {
get {
return string.hashValue
}
}
static func ==(lhs: MyGenericStruct, rhs: MyGenericStruct) -> Bool {
return lhs.hashValue == rhs.hashValue
}
}
Then, declare extension with constraints specified by a where clause for MyProtocol
extension Set where Element: MyProtocol {
func getFirstWith(string: String) -> Element? {
return filter({$0.string == string}).first
}
}
Finally, let's do a test and see its result.
// Example
let set: Set = [
MyGenericStruct(string: "Watermelon"),
MyGenericStruct(string: "Orange"),
MyGenericStruct(string: "Banana")
]
let output = set.getFirstWith(string: "Orange")
print(output)
In my playground with Xcode 8, I can get Optional(MyGenericStruct(string: "Orange"))
in log.
[UPDATED1]
To make an Extension
on Set<MyGenericStruct>
only:
extension Set where Element == MyGenericStruct {
func getFirstWith(string: String) -> Element? {
return filter({$0.string == string}).first
}
}
[UPDATED2]
In case to keep MyGenericStruct<T: MyProtocol>
declaration as stated is necessary, another approach to implement Set
extension:
protocol MyProtocol {
}
struct BaseStruct1: MyProtocol {
}
struct BaseStruct2: MyProtocol {
}
protocol ContainStringProtocol: Hashable {
var string: String { get }
}
struct MyGenericStruct<T: MyProtocol>: ContainStringProtocol {
var string: String
var hashValue: Int {
get {
return string.hashValue
}
}
init(string: String) {
self.string = string
}
static func ==(lhs: MyGenericStruct, rhs: MyGenericStruct) -> Bool {
return lhs.hashValue == rhs.hashValue
}
}
extension Set where Element: ContainStringProtocol {
func getFirstWith(string: String) -> Element? {
return filter({$0.string == string}).first
}
}
// Example
let set1: Set = [
MyGenericStruct<BaseStruct1>(string: "Watermelon"),
MyGenericStruct<BaseStruct1>(string: "Orange"),
MyGenericStruct<BaseStruct1>(string: "Banana")
]
let output1 = set1.getFirstWith(string: "Orange")
print(output1!)
let set2: Set = [
MyGenericStruct<BaseStruct2>(string: "Watermelon"),
MyGenericStruct<BaseStruct2>(string: "Orange"),
MyGenericStruct<BaseStruct2>(string: "Banana")
]
let output2 = set2.getFirstWith(string: "Orange")
print(output2!)
Upvotes: 1
Reputation: 6361
Answering my own question here on request.
I was unable to figure out how to make this an extension on Set
.
Instead, we wrote a little utility function. Not as clear as a extension, but gets the work done. It looks like this:
func getFirst<T: MyProtocol>(fromSet set: Set<MyGenericStruct<T>>, whereStringIs string: String) -> MyGenericStruct<T>? {
return set.first(where: { $0.string == string })
}
As others have noted, MyGenericStruct
needs to be Hashable.
Upvotes: 1
Reputation: 25294
Swift 3.1, xCode 8.3.3
import Foundation
struct MyStruct:Hashable {
let string: String
static func == (lhs: MyStruct, rhs: MyStruct) -> Bool {
return lhs.string == rhs.string
}
var hashValue: Int {
return string.hash
}
}
extension Set where Element == MyStruct {
func getFirstWith(string: String) -> Element? {
return filter({ $0.string == string }).first
}
}
var set = Set<MyStruct>()
set.insert(MyStruct(string: "123"))
set.insert(MyStruct(string: "231"))
print(String(describing:set.getFirstWith(string: "123")))
print(String(describing:set.getFirstWith(string: "231")))
///////////////////////////////////
protocol MyProtocol {}
struct MyType1: MyProtocol {}
struct MyType2: MyProtocol {}
struct MyGenericStruct<T: MyProtocol>: Hashable {
let string: String
static func == (lhs: MyGenericStruct, rhs: MyGenericStruct) -> Bool {
return lhs.string == rhs.string
}
var hashValue: Int {
return string.hash
}
}
extension Set where Element == AnyHashable {
func getFirstWith(string: String) -> Element? {
return filter{ element -> Bool in
if let _element = element as? MyGenericStruct<MyType1> {
if _element.string == string {
return true
}
}
if let _element = element as? MyGenericStruct<MyType2> {
if _element.string == string {
return true
}
}
return false
}.first
}
}
var set2 = Set<AnyHashable>()
set2.insert(MyGenericStruct<MyType1>(string: "abc"))
set2.insert(MyGenericStruct<MyType2>(string: "cba"))
print(String(describing: set2.getFirstWith(string: "abc")))
print(String(describing:set2.getFirstWith(string: "cba")))
Upvotes: 1