Comparing protocol references - arrays

I have an array of protocols. now I want to remove an item from the array, by finding the index of the protocol with the array. however, when comparing the protocol object with the items in the array, the compiler warns with:
'Protocol' does not conform to AnyObject
protocol SomeProtocol {}
var list:[SomeProtocol] = []
func add(some:SomeProtocol) { list+=some }
func remove(some:SomeProtocol) {
var index = -1
for i in 0...list.count-1 { if [i] === some { index = i } }
if index >= 0 { list.removeAtIndex(index) }
}

If you derive only classes for the protocol, you can change protocol definition to:
protocol SomeProtocol: class {}
Then you will be able to use references with this protocol.

First of all, doing add is super easy, just include this function to make it work:
func +=(inout lhs: [SomeProtocol], rhs: SomeProtocol) {
lhs.append(rhs)
}
Doing remove is a lot trickier because SomeProtocol could apply equally to a class or struct, and only values with class types can be compared with ===.
We could use the == operator instead, but it only takes values that conform to the Equatable protocol, and Equatable can only be used as a generic constraint (so far), otherwise you could use something like protocol<SomeProtocol,Equatable> as your array element type.
If you know for sure that your SomeProtocol will only be applied to classes, consider refactoring your code to work with that class type instead:
protocol SomeProtocol {}
class SomeClass : SomeProtocol {}
var list:[SomeClass] = []
func add(some:SomeClass) {
list += some
}
func remove(some:SomeClass) {
list -= some
}
func +=(inout lhs: [SomeClass], rhs: SomeClass) {
lhs.append(rhs)
}
func -=(inout lhs: [SomeClass], rhs: SomeClass) {
for (i,v) in enumerate(lhs) {
if v === rhs {
lhs.removeAtIndex(i)
break
}
}
}
If you also happen to make SomeClass conform to Equatable the usual array functions will work automatically, and you won't even need to overload += and -=.
Otherwise, if you can't know whether your value will be a class or a struct, it might be better to think about what it means for values of SomeProtocol to be "equal" and require a comparison method:
protocol SomeProtocol {
func isEqualTo(some: SomeProtocol) -> Bool
}
func -=(inout lhs: [SomeProtocol], rhs: SomeProtocol) {
for (i,v) in enumerate(lhs) {
if v.isEqualTo(rhs) {
lhs.removeAtIndex(i)
break
}
}
}
// add functions work the same as above
Alternatively, you could use your original code and write a global comparison function:
func ===(lhs: SomeProtocol, rhs: SomeProtocol) -> Bool {
// return something based on the properties of SomeProtocol
}

I end up using isEqual(to: ) in my protocols to test for instance comparisons:
public protocol fooProtocol {
...
func isEqual(to: fooProtocol) -> Bool
}
public extension fooProtocol {
func isEqual(to: fooProtocol) -> Bool {
let ss = self as! NSObject
let tt = to as! NSObject
return ss === tt
}
}
Seems to work for me.

Related

How to check if two arrays made from different structs have the same value?

I've got two arrays made from different structs and both of them have the same information in different positions of the array.
I've tried making a double for-in loop with an if statement to check that they're the same value, but the way that I do it in other languages like PHP is not working.
Please help me, here's what I've got so far:
Singleton One File
struct oneStruct {
var oneTitle: String
var onePic: UIImage?
var oneCategory: String
}
class OneSingleton {
static let sharedInstance = OneSingleton()
private init() {}
var oneArray: [OneStruct] = [//you get the gist, this is full of array stuff]
}
Singleton Two File
struct TwoStruct {
var twoName: String
var twoPic: UIImage?
var twoSubject: String
var twoStuff1: String
// etc. I can't get into much more here, sorry.
}
class TwoSingleton {
static let sharedInstance = TwoSingleton()
private init() {}
var twoArray: [TwoStruct] = [//you get the gist, this is full of array stuff as well]
}
View Controller
//the singleton stuff is needed
var oneArray = [OneSingleton.sharedInstance.oneArray]
var twoArray = [TwoSingleton.sharedInstance.twoArray]
var filterArray: [TwoStruct] = []
override func viewDidLoad() {
super.viewDidLoad()
for two in twoArray {
for one in oneArray {
if two == one {
filterArray.append(contentsOf: two)
}
}
}
}
It's giving me a compile error
Binary operator '==' cannot be applied to operands of type '[TwoStruct]' and '[OneStruct]'
I hope this all makes sense.
Thanks in advance.
To compare implement Equatable for both and change the lhs or rhs to the other object
struct OneStruct :Equatable {
var value1: String
var value2: UIImage? /// image comparsion is not an easy way you may add id of image
var value3: String
// also you can omit any static method part from any class
static func ==(lhs: TwoStruct, rhs: OneStruct) -> Bool {
return lhs.value1 == rhs.value1 && lhs.value3 == rhs.value3
}
}
struct TwoStruct :Equatable {
var value1: String
var value2: UIImage? /// image comparsion is not an easy way you may add id of image
var value3: String
// also you can omit any static method part from any class
static func ==(lhs: OneStruct, rhs: TwoStruct) -> Bool {
return lhs.value1 == rhs.value1 && lhs.value3 == rhs.value3
}
}
//
let s1 = OneStruct(value1: "", value2: UIImage.init(named: ""), value3: "")
let s2 = TwoStruct(value1: "", value2: UIImage.init(named: ""), value3: "")
if s1 == s2 {
}
It seems you have, for each of your types OneStruct and TwoStruct, one respective property that you would like to use for comparison; category and subject, respectively. So to start out, lets blueprint two protocols for these two properties:
protocol Categorizable {
var category: String { get }
}
protocol Subjectable {
var subject: String { get }
}
For the purpose of this example, minimal examples of OneStruct and TwoStruct would then conform to Categorizable and Subjectable, respectively:
struct OneStruct: Categorizable {
var category: String
init(_ category: String) { self.category = category }
}
struct TwoStruct: Subjectable {
var subject: String
init(_ subject: String) { self.subject = subject }
}
Now, we don't really want to talk about equality when comparing two different types, rather equivalent classes or bi-predicates over which instances of these two types can be compared. So let's define e.g. a bi-predicate that can be used to compare two instances conforming to Categorizable and Subjectable, respectively:
struct BiPredicate<T: Categorizable, U: Subjectable> {
static func compare(_ lhs: T, _ rhs: U) -> Bool {
return lhs.category == rhs.subject
}
static func compare(_ lhs: U, _ rhs: T) -> Bool {
return compare(rhs, lhs)
}
}
Based on your own example code, it seems that you want two filter an array of TwoStruct (namely, an array of a type conforming to Subjectable) based on whether, for each element in the array, there exist a member in an array of OneStruct (namely, an array of a type conforming to Categorizable) for which the equivalent class or bi-predicate holds true.
/// Returns `lhs` argument filtered based on its BiPredicate intersection
/// with the `rhs` argument.
/// Time complexity: O(n^2)
func filterByIntersect<T: Subjectable, U: Categorizable>(_ lhs: [T], over rhs: [U]) -> [T] {
return lhs.filter { l in rhs.contains { r in BiPredicate.compare(l, r) } }
}
Which we may use as:
let oneArray = [OneStruct("foo"), OneStruct("bar"), OneStruct("baz"), OneStruct("bax")]
let twoArray = [TwoStruct("baxx"), TwoStruct("foo"), TwoStruct("bax")]
let result = filterByIntersect(twoArray, over: oneArray)
for twoElement in result { print(twoElement.subject) } /* foo
bax */
Now, this was mostly a somewhat interesting exercise, but in the end ended up in quite a lot of boilerplate code for something that should be quite a simple task. You could always drop the generics and just go with a simple function for testing a single equivalent class over OneStruct and TwoStruct:
func compareCategoryWithSubject(_ lhs: OneStruct, _ rhs: TwoStruct) -> Bool {
return lhs.category == rhs.subject
}
/// Returns `lhs` argument filtered based on intersection over comparing
/// the `subject` properties of the elements of `lhs` with the `category`
/// properties of the elements of `rhs`.
/// Time complexity: O(n^2)
func filterByIntersect(_ lhs: [TwoStruct], over rhs: [OneStruct]) -> [TwoStruct] {
return lhs.filter { l in rhs.contains { r in
compareCategoryWithSubject(r, l) } }
}
Or, for a possibly even simple and also asymptotically more performant approach, placing all the logic into the filterByIntersect(...) function:
/// Returns `lhs` argument filtered based on intersection over comparing
/// the `subject` properties of the elements of `lhs` with the `category`
/// properties of the elements of `rhs`.
/// Time complexity: O(n) [contains(_:) is O(1) for Set)
func filterByIntersect(_ lhs: [TwoStruct], over rhs: [OneStruct]) -> [TwoStruct] {
let rhsCategories = Set(rhs.map { $0.category })
return lhs.filter { l in rhsCategories.contains(l.subject) }
}
Both the above yielding the same result as above:
let oneArray = [OneStruct("foo"), OneStruct("bar"), OneStruct("baz"), OneStruct("bax")]
let twoArray = [TwoStruct("baxx"), TwoStruct("foo"), TwoStruct("bax")]
let result = filterByIntersect(twoArray, over: oneArray)
for twoElement in result { print(twoElement.subject) } /* foo
bax */

Swift generics and subclasses

my question might be simple, but it got me puzzled a bit:
Imagine I have an array of different objects which all have a common parent class "MyClass".
var values = [MyClass]()
However they all have their specific subclass like for example "MyClassSubclass".
values.append(MyClassSubclass())
I now want to create a generic method returning me the first object inside this array which is of type MyClassSubclass. I would like to prevent casting the object, but instead have a generic method which takes the subclass object as T parameter and returns me the first occurrence of subclass T inside this array.
I thought of something like this (but surely that does not work):
func getFirst<T: MyClass>(_ ofType : T.Type) -> T?
I guess I'm just stuck and I don't know what to search for, so if someone could help me I would greatly appreciate it.
Edit Example based on the above values:
class MyClass {}
class MyClassSubclass : MyClass {}
class MyClassSubclass2 : MyClass{}
var values = [MyClass]()
values.append(MyClassSubclass())
values.append(MyClassSubclass2())
//Return the first class element appearing here as a subclass type
func getFirst<T>(_ ofType : T.Type) -> T?{}
Thanks
One approach is to iterate over the array and use optional binding
to check if an element is of the given type:
func getFirst<T: MyClass>(ofType: T.Type) -> T? {
for elem in values {
if let item = elem as? T {
return item
}
}
return nil
}
This can be simplified using for case with the as pattern:
func getFirst<T: MyClass>(ofType: T.Type) -> T? {
for case let item as T in values {
return item
}
return nil
}
Another approach is to use flatMap to find all items of the given
type and return the first one:
func getFirst<T: MyClass>(ofType: T.Type) -> T? {
return values.flatMap { $0 as? T }.first
}
If the array can be large and you want to avoid the creation of an
intermediate array then you can use lazy:
func getFirst<T: MyClass>(ofType: T.Type) -> T? {
return values.lazy.flatMap { $0 as? T }.first
}
As an array extension method this would be
extension Array {
func getFirst<T>(ofType: T.Type) -> T? {
return flatMap { $0 as? T }.first
}
}
If you'd like to use global function, which is not recommended, try this
func getFirst<T>(ofType: T.Type) -> T? where T: MyClass {
for value in values where value is T {
return value as? T
}
return nil
}
let first = getFirst(ofType: MyClassSubclass2.self)
The first answer should be better for Swift.
This feels a bit like abuse of generics, but here's an extension:
extension Array where Element == MyClass {
func getFirst<T>(_ ofType: T.Type) -> T? {
return self.first(where: { ofType == type(of: $0) }) as? T
}
}
The method can then be called as let first = values.getFirst(MyClassSubclass.self).
I'd personally prefer simply casting inline for clarity:
let first = values.first(where: { type(of: $0) == MyClassSubclass.self }) as? MyClassSubclass

Insert generic object into array swift [duplicate]

How can I extend Swift's Array<T> or T[] type with custom functional utils?
Browsing around Swift's API docs shows that Array methods are an extension of the T[], e.g:
extension T[] : ArrayType {
//...
init()
var count: Int { get }
var capacity: Int { get }
var isEmpty: Bool { get }
func copy() -> T[]
}
When copying and pasting the same source and trying any variations like:
extension T[] : ArrayType {
func foo(){}
}
extension T[] {
func foo(){}
}
It fails to build with the error:
Nominal type T[] can't be extended
Using the full type definition fails with Use of undefined type 'T', i.e:
extension Array<T> {
func foo(){}
}
And it also fails with Array<T : Any> and Array<String>.
Curiously Swift lets me extend an untyped array with:
extension Array {
func each(fn: (Any) -> ()) {
for i in self {
fn(i)
}
}
}
Which it lets me call with:
[1,2,3].each(println)
But I can't create a proper generic type extension as the type seems to be lost when it flows through the method, e.g trying to replace Swift's built-in filter with:
extension Array {
func find<T>(fn: (T) -> Bool) -> T[] {
var to = T[]()
for x in self {
let t = x as T
if fn(t) {
to += t
}
}
return to
}
}
But the compiler treats it as untyped where it still allows calling the extension with:
["A","B","C"].find { $0 > "A" }
And when stepped-thru with a debugger indicates the type is Swift.String but it's a build error to try access it like a String without casting it to String first, i.e:
["A","B","C"].find { ($0 as String).compare("A") > 0 }
Does anyone know what's the proper way to create a typed extension method that acts like the built-in extensions?
For extending typed arrays with classes, the below works for me (Swift 2.2). For example, sorting a typed array:
class HighScoreEntry {
let score:Int
}
extension Array where Element == HighScoreEntry {
func sort() -> [HighScoreEntry] {
return sort { $0.score < $1.score }
}
}
Trying to do this with a struct or typealias will give an error:
Type 'Element' constrained to a non-protocol type 'HighScoreEntry'
Update:
To extend typed arrays with non-classes use the following approach:
typealias HighScoreEntry = (Int)
extension SequenceType where Generator.Element == HighScoreEntry {
func sort() -> [HighScoreEntry] {
return sort { $0 < $1 }
}
}
In Swift 3 some types have been renamed:
extension Sequence where Iterator.Element == HighScoreEntry
{
// ...
}
After a while trying different things the solution seems to remove the <T> from the signature like:
extension Array {
func find(fn: (T) -> Bool) -> [T] {
var to = [T]()
for x in self {
let t = x as T;
if fn(t) {
to += t
}
}
return to
}
}
Which now works as intended without build errors:
["A","B","C"].find { $0.compare("A") > 0 }
Extend all types:
extension Array where Element: Any {
// ...
}
Extend Comparable types:
extension Array where Element: Comparable {
// ...
}
Extend some types:
extension Array where Element: Comparable & Hashable {
// ...
}
Extend a particular type:
extension Array where Element == Int {
// ...
}
I had a similar problem - wanted to extend the general Array with a swap() method, which was supposed to take an argument of the same type as the array. But how do you specify the generic type? I found by trial and error that the below worked:
extension Array {
mutating func swap(x:[Element]) {
self.removeAll()
self.appendContentsOf(x)
}
}
The key to it was the word 'Element'. Note that I didn't define this type anywhere, it seems automatically exist within the context of the array extension, and refer to whatever the type of the array's elements is.
I am not 100% sure what's going on there, but I think it is probably because 'Element' is an associated type of the Array (see 'Associated Types' here https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Generics.html#//apple_ref/doc/uid/TP40014097-CH26-ID189)
However, I can't see any reference of this in the Array structure reference (https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_Array_Structure/index.html#//apple_ref/swift/struct/s:Sa)... so I'm still a little unsure.
Using Swift 2.2:
I ran into a similar issue when trying to remove duplicates from an array of strings. I was able to add an extension on the Array class that does just what I was looking to do.
extension Array where Element: Hashable {
/**
* Remove duplicate elements from an array
*
* - returns: A new array without duplicates
*/
func removeDuplicates() -> [Element] {
var result: [Element] = []
for value in self {
if !result.contains(value) {
result.append(value)
}
}
return result
}
/**
* Remove duplicate elements from an array
*/
mutating func removeDuplicatesInPlace() {
var result: [Element] = []
for value in self {
if !result.contains(value) {
result.append(value)
}
}
self = result
}
}
Adding these two methods to the Array class allows me to call one of the two methods on an array and successfully remove duplicates. Note that the elements in the array must conform to the Hashable protocol. Now I can do this:
var dupes = ["one", "two", "two", "three"]
let deDuped = dupes.removeDuplicates()
dupes.removeDuplicatesInPlace()
// result: ["one", "two", "three"]
If you want to learn about extending Arrays and other types of build in classes checkout code in this github repo https://github.com/ankurp/Cent
As of Xcode 6.1 the syntax to extend arrays is as follows
extension Array {
func at(indexes: Int...) -> [Element] {
... // You code goes herer
}
}
I had a look at the Swift 2 standard library headers, and here is the prototype for the filter function, which makes it quite obvious how to roll your own.
extension CollectionType {
func filter(#noescape includeElement: (Self.Generator.Element) -> Bool) -> [Self.Generator.Element]
}
It's not an extension to Array, but to CollectionType, so the same method applies to other collection types. #noescape means that the block passed in will not leave the scope of the filter function, which enables some optimisations. Self with a capital S is the class we are extending. Self.Generator is an iterator that iterates through the objects in the collection and Self.Generator.Element is the type of the objects, for example for an array [Int?] Self.Generator.Element would be Int?.
All in all this filter method can be applied to any CollectionType, it needs a filter block which takes an element of the collection and returns a Bool, and it returns an array of the original type. So putting this together, here's a method that I find useful: It combines map and filter, by taking a block that maps a collection element to an optional value, and returns an array of those optional values that are not nil.
extension CollectionType {
func mapfilter<T>(#noescape transform: (Self.Generator.Element) -> T?) -> [T] {
var result: [T] = []
for x in self {
if let t = transform (x) {
result.append (t)
}
}
return result
}
}
import Foundation
extension Array {
var randomItem: Element? {
let idx = Int(arc4random_uniform(UInt32(self.count)))
return self.isEmpty ? nil : self[idx]
}
}
(Swift 2.x)
You can also extend the array to conform to a protocol containing blue-rpints for generic type methods, e.g., a protocol containing your custom functional utils for all generic array elements conforming to some type constraint, say protocol MyTypes. The bonus using this approach is that you can write functions taking generic array arguments, with a constraint that these array arguments must conform to your custom function utilities protocol, say protocol MyFunctionalUtils.
You can get this behaviour either implicitly, by type constraining the array elements to MyTypes, or---as I will show in the method I describe below---, quite neatly, explicitly, letting your generic array functions header directly show that input arrays conforms to MyFunctionalUtils.
We begin with Protocols MyTypes for use as type constraint; extend the types you want to fit in your generics by this protocol (example below extends fundamental types Int and Double as well as a custom type MyCustomType)
/* Used as type constraint for Generator.Element */
protocol MyTypes {
var intValue: Int { get }
init(_ value: Int)
func *(lhs: Self, rhs: Self) -> Self
func +=(inout lhs: Self, rhs: Self)
}
extension Int : MyTypes { var intValue: Int { return self } }
extension Double : MyTypes { var intValue: Int { return Int(self) } }
// ...
/* Custom type conforming to MyTypes type constraint */
struct MyCustomType : MyTypes {
var myInt : Int? = 0
var intValue: Int {
return myInt ?? 0
}
init(_ value: Int) {
myInt = value
}
}
func *(lhs: MyCustomType, rhs: MyCustomType) -> MyCustomType {
return MyCustomType(lhs.intValue * rhs.intValue)
}
func +=(inout lhs: MyCustomType, rhs: MyCustomType) {
lhs.myInt = (lhs.myInt ?? 0) + (rhs.myInt ?? 0)
}
Protocol MyFunctionalUtils (holding blueprints our additional generic array functions utilities) and thereafter, the extension of Array by MyFunctionalUtils; implementation of blue-printed method(s):
/* Protocol holding our function utilities, to be used as extension
o Array: blueprints for utility methods where Generator.Element
is constrained to MyTypes */
protocol MyFunctionalUtils {
func foo<T: MyTypes>(a: [T]) -> Int?
// ...
}
/* Extend array by protocol MyFunctionalUtils and implement blue-prints
therein for conformance */
extension Array : MyFunctionalUtils {
func foo<T: MyTypes>(a: [T]) -> Int? {
/* [T] is Self? proceed, otherwise return nil */
if let b = self.first {
if b is T && self.count == a.count {
var myMultSum: T = T(0)
for (i, sElem) in self.enumerate() {
myMultSum += (sElem as! T) * a[i]
}
return myMultSum.intValue
}
}
return nil
}
}
Finally, tests and two examples showing a function taking generic arrays, with the following cases, respectively
Showing implicit assertion that the array parameters conform to protocol 'MyFunctionalUtils', via type constraining the arrays elements to 'MyTypes' (function bar1).
Showing explicitly that the array parameters conform to protocol 'MyFunctionalUtils' (function bar2).
The test and examples follows:
/* Tests & examples */
let arr1d : [Double] = [1.0, 2.0, 3.0]
let arr2d : [Double] = [-3.0, -2.0, 1.0]
let arr1my : [MyCustomType] = [MyCustomType(1), MyCustomType(2), MyCustomType(3)]
let arr2my : [MyCustomType] = [MyCustomType(-3), MyCustomType(-2), MyCustomType(1)]
/* constrain array elements to MyTypes, hence _implicitly_ constraining
array parameters to protocol MyFunctionalUtils. However, this
conformance is not apparent just by looking at the function signature... */
func bar1<U: MyTypes> (arr1: [U], _ arr2: [U]) -> Int? {
return arr1.foo(arr2)
}
let myInt1d = bar1(arr1d, arr2d) // -4, OK
let myInt1my = bar1(arr1my, arr2my) // -4, OK
/* constrain the array itself to protocol MyFunctionalUtils; here, we
see directly in the function signature that conformance to
MyFunctionalUtils is given for valid array parameters */
func bar2<T: MyTypes, U: protocol<MyFunctionalUtils, _ArrayType> where U.Generator.Element == T> (arr1: U, _ arr2: U) -> Int? {
// OK, type U behaves as array type with elements T (=MyTypes)
var a = arr1
var b = arr2
a.append(T(2)) // add 2*7 to multsum
b.append(T(7))
return a.foo(Array(b))
/* Ok! */
}
let myInt2d = bar2(arr1d, arr2d) // 10, OK
let myInt2my = bar2(arr1my, arr2my) // 10, OK
import Foundation
extension Array {
func calculateMean() -> Double {
// is this an array of Doubles?
if self.first is Double {
// cast from "generic" array to typed array of Doubles
let doubleArray = self.map { $0 as! Double }
// use Swift "reduce" function to add all values together
let total = doubleArray.reduce(0.0, combine: {$0 + $1})
let meanAvg = total / Double(self.count)
return meanAvg
} else {
return Double.NaN
}
}
func calculateMedian() -> Double {
// is this an array of Doubles?
if self.first is Double {
// cast from "generic" array to typed array of Doubles
var doubleArray = self.map { $0 as! Double }
// sort the array
doubleArray.sort( {$0 < $1} )
var medianAvg : Double
if doubleArray.count % 2 == 0 {
// if even number of elements - then mean average the middle two elements
var halfway = doubleArray.count / 2
medianAvg = (doubleArray[halfway] + doubleArray[halfway - 1]) / 2
} else {
// odd number of elements - then just use the middle element
medianAvg = doubleArray[doubleArray.count / 2 ]
}
return medianAvg
} else {
return Double.NaN
}
}
}
Extention Array Find Index:
extension Array where Element: Equatable {
func findElementArrayIndex(findElement: String) -> Int {
var indexValue: Int = 0
var search = self.filter { findElement.isEmpty || "\($0)".contains(findElement)}
//print("search: \(search)")
for i in 0..<self.count {
if self[i] == search[0] {
indexValue = i
break
}
}
return indexValue
}
}

Remove array items from array

I'm trying to write an array extension that will allow me to remove items from an array if they exist in another array. I will need to use this for a range of custom types so was trying to build something generic.
This is what I have so far which returns the error "binary operator == cannot be applied to two t operands"
extension Array{
mutating func removeArrayItems<T>(itemsToRemove: [T]){
for item in itemsToRemove{
var removed = removeObject(item)
}
}
mutating func removeObject<T>(object: T) -> Bool {
for (idx, objectToCompare) in enumerate(self) {
if let to = objectToCompare as? T {
if object == to {
self.removeAtIndex(idx)
return true
}
}
}
return false
}
}
The equality operator, ==, only applies to objects that are Equatable. You need to qualify your type T as T:Equatable. As such:
mutating func removeObject<T:Equatable>(object: T) -> Bool {
// ...
}
Same with removeArrayItems
Note that if you are defining your own types, such as with class or struct, your types will also need to conform to Equatable. Like this:
class Foo : Equatable {
// ...
}
public func ==<T:Foo> (lhs:T, rhs:T) -> Bool {
// ...
}

How can I extend typed Arrays in Swift?

How can I extend Swift's Array<T> or T[] type with custom functional utils?
Browsing around Swift's API docs shows that Array methods are an extension of the T[], e.g:
extension T[] : ArrayType {
//...
init()
var count: Int { get }
var capacity: Int { get }
var isEmpty: Bool { get }
func copy() -> T[]
}
When copying and pasting the same source and trying any variations like:
extension T[] : ArrayType {
func foo(){}
}
extension T[] {
func foo(){}
}
It fails to build with the error:
Nominal type T[] can't be extended
Using the full type definition fails with Use of undefined type 'T', i.e:
extension Array<T> {
func foo(){}
}
And it also fails with Array<T : Any> and Array<String>.
Curiously Swift lets me extend an untyped array with:
extension Array {
func each(fn: (Any) -> ()) {
for i in self {
fn(i)
}
}
}
Which it lets me call with:
[1,2,3].each(println)
But I can't create a proper generic type extension as the type seems to be lost when it flows through the method, e.g trying to replace Swift's built-in filter with:
extension Array {
func find<T>(fn: (T) -> Bool) -> T[] {
var to = T[]()
for x in self {
let t = x as T
if fn(t) {
to += t
}
}
return to
}
}
But the compiler treats it as untyped where it still allows calling the extension with:
["A","B","C"].find { $0 > "A" }
And when stepped-thru with a debugger indicates the type is Swift.String but it's a build error to try access it like a String without casting it to String first, i.e:
["A","B","C"].find { ($0 as String).compare("A") > 0 }
Does anyone know what's the proper way to create a typed extension method that acts like the built-in extensions?
For extending typed arrays with classes, the below works for me (Swift 2.2). For example, sorting a typed array:
class HighScoreEntry {
let score:Int
}
extension Array where Element == HighScoreEntry {
func sort() -> [HighScoreEntry] {
return sort { $0.score < $1.score }
}
}
Trying to do this with a struct or typealias will give an error:
Type 'Element' constrained to a non-protocol type 'HighScoreEntry'
Update:
To extend typed arrays with non-classes use the following approach:
typealias HighScoreEntry = (Int)
extension SequenceType where Generator.Element == HighScoreEntry {
func sort() -> [HighScoreEntry] {
return sort { $0 < $1 }
}
}
In Swift 3 some types have been renamed:
extension Sequence where Iterator.Element == HighScoreEntry
{
// ...
}
After a while trying different things the solution seems to remove the <T> from the signature like:
extension Array {
func find(fn: (T) -> Bool) -> [T] {
var to = [T]()
for x in self {
let t = x as T;
if fn(t) {
to += t
}
}
return to
}
}
Which now works as intended without build errors:
["A","B","C"].find { $0.compare("A") > 0 }
Extend all types:
extension Array where Element: Any {
// ...
}
Extend Comparable types:
extension Array where Element: Comparable {
// ...
}
Extend some types:
extension Array where Element: Comparable & Hashable {
// ...
}
Extend a particular type:
extension Array where Element == Int {
// ...
}
I had a similar problem - wanted to extend the general Array with a swap() method, which was supposed to take an argument of the same type as the array. But how do you specify the generic type? I found by trial and error that the below worked:
extension Array {
mutating func swap(x:[Element]) {
self.removeAll()
self.appendContentsOf(x)
}
}
The key to it was the word 'Element'. Note that I didn't define this type anywhere, it seems automatically exist within the context of the array extension, and refer to whatever the type of the array's elements is.
I am not 100% sure what's going on there, but I think it is probably because 'Element' is an associated type of the Array (see 'Associated Types' here https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Generics.html#//apple_ref/doc/uid/TP40014097-CH26-ID189)
However, I can't see any reference of this in the Array structure reference (https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_Array_Structure/index.html#//apple_ref/swift/struct/s:Sa)... so I'm still a little unsure.
Using Swift 2.2:
I ran into a similar issue when trying to remove duplicates from an array of strings. I was able to add an extension on the Array class that does just what I was looking to do.
extension Array where Element: Hashable {
/**
* Remove duplicate elements from an array
*
* - returns: A new array without duplicates
*/
func removeDuplicates() -> [Element] {
var result: [Element] = []
for value in self {
if !result.contains(value) {
result.append(value)
}
}
return result
}
/**
* Remove duplicate elements from an array
*/
mutating func removeDuplicatesInPlace() {
var result: [Element] = []
for value in self {
if !result.contains(value) {
result.append(value)
}
}
self = result
}
}
Adding these two methods to the Array class allows me to call one of the two methods on an array and successfully remove duplicates. Note that the elements in the array must conform to the Hashable protocol. Now I can do this:
var dupes = ["one", "two", "two", "three"]
let deDuped = dupes.removeDuplicates()
dupes.removeDuplicatesInPlace()
// result: ["one", "two", "three"]
If you want to learn about extending Arrays and other types of build in classes checkout code in this github repo https://github.com/ankurp/Cent
As of Xcode 6.1 the syntax to extend arrays is as follows
extension Array {
func at(indexes: Int...) -> [Element] {
... // You code goes herer
}
}
I had a look at the Swift 2 standard library headers, and here is the prototype for the filter function, which makes it quite obvious how to roll your own.
extension CollectionType {
func filter(#noescape includeElement: (Self.Generator.Element) -> Bool) -> [Self.Generator.Element]
}
It's not an extension to Array, but to CollectionType, so the same method applies to other collection types. #noescape means that the block passed in will not leave the scope of the filter function, which enables some optimisations. Self with a capital S is the class we are extending. Self.Generator is an iterator that iterates through the objects in the collection and Self.Generator.Element is the type of the objects, for example for an array [Int?] Self.Generator.Element would be Int?.
All in all this filter method can be applied to any CollectionType, it needs a filter block which takes an element of the collection and returns a Bool, and it returns an array of the original type. So putting this together, here's a method that I find useful: It combines map and filter, by taking a block that maps a collection element to an optional value, and returns an array of those optional values that are not nil.
extension CollectionType {
func mapfilter<T>(#noescape transform: (Self.Generator.Element) -> T?) -> [T] {
var result: [T] = []
for x in self {
if let t = transform (x) {
result.append (t)
}
}
return result
}
}
import Foundation
extension Array {
var randomItem: Element? {
let idx = Int(arc4random_uniform(UInt32(self.count)))
return self.isEmpty ? nil : self[idx]
}
}
(Swift 2.x)
You can also extend the array to conform to a protocol containing blue-rpints for generic type methods, e.g., a protocol containing your custom functional utils for all generic array elements conforming to some type constraint, say protocol MyTypes. The bonus using this approach is that you can write functions taking generic array arguments, with a constraint that these array arguments must conform to your custom function utilities protocol, say protocol MyFunctionalUtils.
You can get this behaviour either implicitly, by type constraining the array elements to MyTypes, or---as I will show in the method I describe below---, quite neatly, explicitly, letting your generic array functions header directly show that input arrays conforms to MyFunctionalUtils.
We begin with Protocols MyTypes for use as type constraint; extend the types you want to fit in your generics by this protocol (example below extends fundamental types Int and Double as well as a custom type MyCustomType)
/* Used as type constraint for Generator.Element */
protocol MyTypes {
var intValue: Int { get }
init(_ value: Int)
func *(lhs: Self, rhs: Self) -> Self
func +=(inout lhs: Self, rhs: Self)
}
extension Int : MyTypes { var intValue: Int { return self } }
extension Double : MyTypes { var intValue: Int { return Int(self) } }
// ...
/* Custom type conforming to MyTypes type constraint */
struct MyCustomType : MyTypes {
var myInt : Int? = 0
var intValue: Int {
return myInt ?? 0
}
init(_ value: Int) {
myInt = value
}
}
func *(lhs: MyCustomType, rhs: MyCustomType) -> MyCustomType {
return MyCustomType(lhs.intValue * rhs.intValue)
}
func +=(inout lhs: MyCustomType, rhs: MyCustomType) {
lhs.myInt = (lhs.myInt ?? 0) + (rhs.myInt ?? 0)
}
Protocol MyFunctionalUtils (holding blueprints our additional generic array functions utilities) and thereafter, the extension of Array by MyFunctionalUtils; implementation of blue-printed method(s):
/* Protocol holding our function utilities, to be used as extension
o Array: blueprints for utility methods where Generator.Element
is constrained to MyTypes */
protocol MyFunctionalUtils {
func foo<T: MyTypes>(a: [T]) -> Int?
// ...
}
/* Extend array by protocol MyFunctionalUtils and implement blue-prints
therein for conformance */
extension Array : MyFunctionalUtils {
func foo<T: MyTypes>(a: [T]) -> Int? {
/* [T] is Self? proceed, otherwise return nil */
if let b = self.first {
if b is T && self.count == a.count {
var myMultSum: T = T(0)
for (i, sElem) in self.enumerate() {
myMultSum += (sElem as! T) * a[i]
}
return myMultSum.intValue
}
}
return nil
}
}
Finally, tests and two examples showing a function taking generic arrays, with the following cases, respectively
Showing implicit assertion that the array parameters conform to protocol 'MyFunctionalUtils', via type constraining the arrays elements to 'MyTypes' (function bar1).
Showing explicitly that the array parameters conform to protocol 'MyFunctionalUtils' (function bar2).
The test and examples follows:
/* Tests & examples */
let arr1d : [Double] = [1.0, 2.0, 3.0]
let arr2d : [Double] = [-3.0, -2.0, 1.0]
let arr1my : [MyCustomType] = [MyCustomType(1), MyCustomType(2), MyCustomType(3)]
let arr2my : [MyCustomType] = [MyCustomType(-3), MyCustomType(-2), MyCustomType(1)]
/* constrain array elements to MyTypes, hence _implicitly_ constraining
array parameters to protocol MyFunctionalUtils. However, this
conformance is not apparent just by looking at the function signature... */
func bar1<U: MyTypes> (arr1: [U], _ arr2: [U]) -> Int? {
return arr1.foo(arr2)
}
let myInt1d = bar1(arr1d, arr2d) // -4, OK
let myInt1my = bar1(arr1my, arr2my) // -4, OK
/* constrain the array itself to protocol MyFunctionalUtils; here, we
see directly in the function signature that conformance to
MyFunctionalUtils is given for valid array parameters */
func bar2<T: MyTypes, U: protocol<MyFunctionalUtils, _ArrayType> where U.Generator.Element == T> (arr1: U, _ arr2: U) -> Int? {
// OK, type U behaves as array type with elements T (=MyTypes)
var a = arr1
var b = arr2
a.append(T(2)) // add 2*7 to multsum
b.append(T(7))
return a.foo(Array(b))
/* Ok! */
}
let myInt2d = bar2(arr1d, arr2d) // 10, OK
let myInt2my = bar2(arr1my, arr2my) // 10, OK
import Foundation
extension Array {
func calculateMean() -> Double {
// is this an array of Doubles?
if self.first is Double {
// cast from "generic" array to typed array of Doubles
let doubleArray = self.map { $0 as! Double }
// use Swift "reduce" function to add all values together
let total = doubleArray.reduce(0.0, combine: {$0 + $1})
let meanAvg = total / Double(self.count)
return meanAvg
} else {
return Double.NaN
}
}
func calculateMedian() -> Double {
// is this an array of Doubles?
if self.first is Double {
// cast from "generic" array to typed array of Doubles
var doubleArray = self.map { $0 as! Double }
// sort the array
doubleArray.sort( {$0 < $1} )
var medianAvg : Double
if doubleArray.count % 2 == 0 {
// if even number of elements - then mean average the middle two elements
var halfway = doubleArray.count / 2
medianAvg = (doubleArray[halfway] + doubleArray[halfway - 1]) / 2
} else {
// odd number of elements - then just use the middle element
medianAvg = doubleArray[doubleArray.count / 2 ]
}
return medianAvg
} else {
return Double.NaN
}
}
}
Extention Array Find Index:
extension Array where Element: Equatable {
func findElementArrayIndex(findElement: String) -> Int {
var indexValue: Int = 0
var search = self.filter { findElement.isEmpty || "\($0)".contains(findElement)}
//print("search: \(search)")
for i in 0..<self.count {
if self[i] == search[0] {
indexValue = i
break
}
}
return indexValue
}
}

Resources