Generics in Sequence Extensions - arrays

I have some code which I'm trying to make generic, and can't quite figure out how.
func randomInt() -> Int { return Int(arc4random_uniform(42)) }
let closures = [randomInt, randomInt, randomInt, randomInt]
let result = closures.map { $0() }
print(result)
In essence, I want to take an array of closures, that return some value of type T, and return an array of type [T] with all the results.
This is my attempt:
extension Sequence where Element == () -> T
{
func callAll() -> [T]
{
return self.map { $0() }
}
}
However, this clearly doesn't work, as T is undefined. How would I go about writing this extension?

You can make the generic callAll function work only on Sequences, where Element is of type ()->T, but as far as I know, you cannot declare a generic type in a where clause to an extension.
extension Sequence {
func callAll<T>() -> [T] where Self.Element == ()->T{
return self.map { $0() }
}
}
func randomInt() -> Int { return Int(arc4random_uniform(42)) }
let closures = [randomInt, randomInt, randomInt, randomInt]
let result = closures.callAll()
print(result) //one sample output: [41, 19, 15, 36]

Related

Variable 'offerCardsShuffled' inferred to have type '()', which may be unexpected

Related to this question, but a different error: Other Question
I have tried both the Swift 3 as well as the Swift 4 code from this question:
Shuffle Link
I am using Swift 3.2, and when using the Swift 3 code it yelled at me for not using SwapAt, so I've included the Swift 4 code, but I tried both, with the same result.
When using the shuffle() method, I get the error Variable 'offerCardsShuffled' inferred to have type '()', which may be unexpected, and the obvious error afterwards, Value of tuple type '()' has no member 'enumerated'
I'm not sure what would be causing this, as other people using this code don't seem to be having that problem. Why are my shuffled arrays coming up as empty tuples?
Note I am fairly new to Swift development so a thorough explanation is definitely helpful.
func displayOfferCards() -> Void {
//let offerCardsr = allOfferCards().reversed()
var offerCards = allOfferCards()
var offerCardsShuffled = offerCards.shuffle()
for (index, offerCard) in offerCardsShuffled.enumerated() {
let delay = Double(index) * 0.2
offerCard.display(delay: delay)
}
}
}
func allOfferCards() -> [OfferCard]{
guard dataSource != nil else {
return []
}
let numberOfCards = self.dataSource!.kolodaNumberOfCards(self)
var offerCards = [OfferCard]()
for i in 0..<numberOfCards {
let offerCard = viewForCard(at: i)
if let offerCard = offerCard {
offerCards.append(offerCard as! OfferCard)
}
}
return offerCards
}
extension MutableCollection {
/// Shuffles the contents of this collection.
mutating func shuffle() {
let c = count
guard c > 1 else { return }
for (firstUnshuffled, unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
let d: IndexDistance = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
let i = index(firstUnshuffled, offsetBy: d)
swapAt(firstUnshuffled, i)
}
}
}
extension Sequence {
/// Returns an array with the contents of this sequence, shuffled.
func shuffled() -> [Element] {
var result = Array(self)
result.shuffle()
return result
}
}
Your shuffle method modifies the original collection, it does not return a new collection.
Change:
var offerCardsShuffled = offerCards.shuffle()
to:
offerCards.shuffle()
and replace the use of offerCardsShuffled with offerCards in the remaining code.

Finding cgpoint in array based only on X

I have an array of CGPoints in Swift.
I'd like to find a point in the array using only the X value (they all have unique X values, so this shouldn't be a problem) and then get the Y value from that.
I also would like a way to only see if it contains a point with the X value that I'm looking for, like a .contains that only cares about the X.
You can use the indexOf: function to find the index of an item in an array using a predicate. From there, you can access that item with the index.
This actually answers both your questions, because it either returns the index of a CGPoint with your X-value, or it returns nil.
let index = pointArray.indexOf {
$0.x == xToFind
}
if let index = index {
let point = pointArray[index]
// Do something with the point that has the x-value you wanted
} else {
// There is no point in the array with the x-value you wanted
}
One overly complicated but sweet way to handle problems like this is with Protocols and Extensions.
If we make a XYType protocol that has a typealias Element and x and y properties of type Element we can extend SequenceType to encapsulate the filter and contains methods.
protocol XYType {
typealias Element
var x : Element { get set }
var y : Element { get set }
}
extension CGPoint : XYType {}
Now the real methods :
Extend SequenceType but use constraints. The Generator.Element should be an XYType and the Generator.Element.Element (the Element of the XYType) should be Equatable.
The actual filter function is a bit complicated. But essentially it uses the function it gets as a parameter includeElement: (Self.Generator.Element.Element) throws -> Bool and if it is true it appends to a copy. In the end it returns that copy.
extension SequenceType where Generator.Element : XYType, Generator.Element.Element : Equatable {
// this function just mimics the regular filter.
func filterByX(#noescape includeElement: (Self.Generator.Element.Element) throws -> Bool) rethrows -> [Self.Generator.Element] {
var copy : [Generator.Element] = []
for element in self where try includeElement(element.x) {
do {
let include = try includeElement(element.x)
if include {
copy.append(element)
}
} catch {
continue
}
}
return copy
}
func filterByY(#noescape includeElement: (Self.Generator.Element.Element) throws -> Bool) rethrows -> [Self.Generator.Element] {
var copy : [Generator.Element] = []
for element in self where try includeElement(element.y) {
do {
let include = try includeElement(element.y)
if include {
copy.append(element)
}
} catch {
continue
}
}
return copy
}
}
Do the same for contains
extension SequenceType where Generator.Element : XYType, Generator.Element.Element : Equatable {
func containsX(#noescape predicate: (Self.Generator.Element.Element) throws -> Bool) rethrows -> Bool {
for element in self {
do {
let result = try predicate(element.x)
if result {
return true
}
} catch {
continue
}
}
return false
}
func containsY(#noescape predicate: (Self.Generator.Element.Element) throws -> Bool) rethrows -> Bool {
for element in self {
do {
let result = try predicate(element.y)
if result {
return true
}
} catch {
continue
}
}
return false
}
}
Tests :
let points = [CGPoint(x: 1, y: 1),CGPoint(x: 2, y: 2),CGPoint(x: 3, y: 3),CGPoint(x: 4, y: 4)]
// CGPoint(2,2)
points.filterByY { (y) -> Bool in
y == 2
}
// false
points.containsX { (x) -> Bool in
x == 5
}
P.S. :
you can also do this, it is a little bit shorter :
let filtered = points.filter {
$0.x == 2
}
let contains = points.contains {
$0.x == 3
}

How to compare two arrays of protocols for equality in Swift?

I've run into a situation that I'm sure is not that uncommon. I have two arrays of objects that conform to a protocol and I want to check if they are the equal.
What I'd really like to do is this:
protocol Pattern: Equatable
{
func isEqualTo(other: Pattern) -> Bool
}
func ==(rhs:Pattern, lhs:Pattern) -> Bool
{
return rhs.isEqualTo(lhs)
}
extension Equatable where Self : Pattern
{
func isEqualTo(other: Pattern) -> Bool
{
guard let o = other as? Self else { return false }
return self == o
}
}
However, this leads to the compile error:
Error:(10, 30) protocol 'Pattern' can only be used as a generic constraint because it has Self or associated type requirements
Based on this post I realise that I need to lose the Equatable inheritance on my protocol and push it down onto the concrete 'Pattern' declarations. Though I really don't understand why. If I'm defining how the two objects are equal based on the protocol by overloading == there really is no issue as far as I can see. I don't even need to know the actual types or whether they are classes or structs.
Regardless, this is all well and good and I can now compare concretePattern.isEqualTo(otherConcretePattern) but the issue remains that I can no longer compare arrays of these objects like I can compare an array of a concrete type as array equality relies on overloading the == operator.
The best I've managed to do so far is glom an isEqualTo method onto CollectionType via an extension. This at least allows me to compare arrays. But frankly, this code stinks.
extension CollectionType where Generator.Element == Pattern
{
func isEqualTo(patterns:[Pattern]) -> Bool {
return self.count as? Int == patterns.count && !zip(self, patterns).contains { !$0.isEqualTo($1) }
}
}
Is there really no other way of doing this? Please tell me I'm missing something obvious.
I have two arrays of objects that conform to a protocol and I want to check if they are the equal.
So you want to say the two arrays are equal if all the elements in them are equal and the elements all conform to pattern. i.e.
If a, b, c and d are all things that conform to Pattern, you want
a == c
a != b
a != d
b != d
let array1: [Pattern] = [a, b, c]
let array2: [Pattern] = [a, b, a]
let array3: [Pattern] = [a, d, c]
array1 == array2 // true
array1 == array3 // false
The easiest way to do this is actually to define an equality operator for two arrays of patterns i.e.
protocol Pattern
{
func isEqualTo(other: Pattern) -> Bool
}
func ==(rhs: Pattern, lhs: Pattern) -> Bool
{
return rhs.isEqualTo(lhs)
}
func ==(lhs: [Pattern], rhs: [Pattern]) -> Bool
{
guard lhs.count == rhs.count else { return false }
var i1 = lhs.generate()
var i2 = rhs.generate()
var isEqual = true
while let e1 = i1.next(), e2 = i2.next() where isEqual
{
isEqual = e1 == e2
}
return isEqual
}
I defined two types that conform to Pattern and tried various equality compares and it all works
struct Foo: Pattern
{
let data: String
init(data: String)
{
self.data = data
}
func isEqualTo(other: Pattern) -> Bool
{
guard let other = other as? Foo else { return false }
return self.data == other.data
}
}
struct Bar: Pattern
{
let data: String
init(data: String)
{
self.data = data
}
func isEqualTo(other: Pattern) -> Bool
{
guard let other = other as? Bar else { return false }
return self.data == other.data
}
}
let a = Foo(data: "jeremyp")
let b = Bar(data: "jeremyp")
let c = Foo(data: "jeremyp")
let d = Foo(data: "jeremy")
let comp1 = a == c // true
let comp2 = a == b // false
let comp3 = a == d // false
let array1: [Pattern] = [a, b, c]
let array2: [Pattern] = [a, b, a]
let array3: [Pattern] = [a, d, c]
let comp4 = array1 == array2 // true
let comp5 = array1 == array3 // false
The Swift answer:
protocol _Pattern
{
func _isEqualTo(_other: Any) -> Bool?
}
extension _Pattern where Self: Pattern
{
func _isEqualTo(_other: Any) -> Bool?
{
return (_other as? Self).map({ self.isEqualTo($0) })
}
}
protocol Pattern: _Pattern, Equatable
{
func isEqualTo(other: Self) -> Bool
}
extension Pattern
{
func isEqualTo(other: _Pattern) -> Bool
{
return _isEqualTo(other) ?? false
}
}
func == <T: Pattern>(rhs: T, lhs: T) -> Bool
{
return rhs.isEqualTo(lhs)
}
This is a pattern (pun intended) that I developed myself, and it works extremely well for situations like these. _Pattern is an auto-implemented protocol courtesy of the new protocol-extension feature, and represents a type-erased version of Pattern.

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
}
}

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