Generics with Generators and Sequences in Swift - ios

I have a fairly good understanding of OO programming and Swift, however, one area that really leaves me stumped is Generators and Sequences (I am fine with the concept of protocols by the way).
For example, I completed this exercise from the Swift Guide (Apple)
“EXPERIMENT Modify the anyCommonElements function to make a function that returns an array of the elements that any two sequences have in common.”
Turning this:
func​ ​anyCommonElements​ <​T​, ​U​ ​where​ ​T​: ​SequenceType​, ​U​: ​SequenceType​, ​T​.​Generator​.​Element​: ​Equatable​, ​T​.​Generator​.​Element​ == ​U​.​Generator​.​Element​> (​lhs​: ​T​, ​rhs​: ​U​) -> ​Bool​ {
for​ ​lhsItem​ ​in​ ​lhs​ {
for​ ​rhsItem​ ​in​ ​rhs​ {
if​ ​lhsItem​ == ​rhsItem​ {
return​ ​true
}
}
}
return​ ​false
}
Into this:
func anyCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, rhs: U) -> [T.Generator.Element]? {
var commonElements:[T.Generator.Element]? = nil
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
if (commonElements == nil)
{
commonElements = [] //init the array if not already
}
commonElements?.append(lhsItem) //add matching item to the array
}
}
}
return commonElements? //return nil or array of matched elements
}
I'm happy with the solution I've written and it works well, including the Optional return, however I am lost as to why the type of the commonElements return array needs to be this:
var commonElements:[T.Generator.Element]
Rather than this:
var commonElements:[T]
I've read a fair bit on the subject, including:
https://schani.wordpress.com/2014/06/06/generators-in-swift/
http://robots.thoughtbot.com/swift-sequences
http://natashatherobot.com/swift-conform-to-sequence-protocol/
But I am still completely lost - can someone please help explain this in simple terms or is it just a little abstract and not easy to describe?
Would really appreciate it,
Thanks, John
Update for Swift 5:
(using Sequence instead of SequenceType, Iterator instead of Generator, and new Where syntax.
func anyCommonElements <T, U> (lhs: T, rhs: U) -> [T.Iterator.Element] where T: Sequence, U: Sequence, T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element {
var commonElements:[T.Iterator.Element] = []
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
commonElements.append(lhsItem) //add matching item to the array
}
}
}
return commonElements //return nil or array of matched elements
}
In fact, you can just do T.Element now, and forget about the Iterator.

T is a sequence type. For simplicity, let's take a special and familiar case and say T is an array.
Then the type of thing contained in the array is T.Generator.Element. This is because of the way the Array struct is defined. Keep in mind that Array is a generic. It is a SequenceType, which is a (generic) protocol with an empty type alias Generator, which is constrained to be a GeneratorType, which in turn is a (generic) protocol that has an empty type alias Element. When the generic is specialized, those empty type aliases are "filled in" with an actual type. All sequences are like that. So if T is an Array, then T.Generator.Element means "the actual type of this array's actual elements".
So [T.Generator.Element] means "an array of the same kind of element as the original array's elements.
Your proposed expression, [T], would mean an array of arrays, which is not what we want.
Okay, now generalize T back to any sequence (array, string of character, etc.) and that explanation keeps on working.

The author's answer is no longer valid for the latest version of Swift. Here is an update that is compatible with version 3.0.1, they have simplified how to make a generic array. note: I originally used [Any] arrays, but updated the code based on the feedback below.
func showCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> [T.Iterator.Element]
where T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element {
var result:[T.Iterator.Element] = []
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
result.append(lhsItem)
}
}
}
return result
}
You can test the code with the following commands:
showCommonElements([1, 2, 3, 4, 5], [4, 7, 3])
showCommonElements(["apple", "banana", "orange", "peach"], ["orange", "pear", "apple"])

Related

Set (Collection) - Insert multiple elements

Set is an unordered collection of unique elements. Almost similar to array.
I want to add/insert multiple elements in a Set of String. But there is only single method provided that can insert only one element (accepts single Set element as a parameter argument) and I've collection of string (id).
insert(_:)
#discardableResult mutating func insert(_ newMember: Set.Element) -> (inserted: Bool, memberAfterInsert: Set.Element)
How can I do that?
What I've tried:
I tried to create an extension very similar to insert(_:) method but it can accept multiple Set elements. It would be same as use of iteration over collection but don't need to handle it manually everywhere.
extension Set {
#discardableResult mutating func insert(_ newMembers: [Set.Element]) -> (inserted: Bool, memberAfterInsert: Set.Element) {
newMembers.forEach { (member) in
self.insert(member)
}
}
}
It should work, if I return a tuple as expected but no idea how and where (which line) and what to return a value.
Here is error message.
Missing return in a function expected to return '(inserted: Bool, memberAfterInsert: Set.Element)'
What can be solution to this. Is there any better solution/approach to handle this operation?
It was pointed out in the comments under the question, but I'd like to clearly state that there is a method for that very same purpose:
mutating func formUnion<S>(_ other: S) where Element == S.Element, S : Sequence
Usage:
var attendees: Set = ["Alicia", "Bethany", "Diana"]
let visitors = ["Diana", "Marcia", "Nathaniel"]
attendees.formUnion(visitors)
print(attendees)
// Prints "["Diana", "Nathaniel", "Bethany", "Alicia", "Marcia"]"
Source: Apple Developer
There is also an immutable variant which returns a new instance containing the union:
func union<S>(_ other: S) -> Set<Set.Element> where Element == S.Element, S : Sequence
Usage:
let attendees: Set = ["Alicia", "Bethany", "Diana"]
let visitors = ["Marcia", "Nathaniel"]
let attendeesAndVisitors = attendees.union(visitors)
print(attendeesAndVisitors)
// Prints "["Diana", "Nathaniel", "Bethany", "Alicia", "Marcia"]"
Source: Apple Developer
Swift Set Union
[Swift Set operations]
a.union(b) - a ∪ b - the result set contains all elements from a and b
union - immutable function
unionInPlace(up to Swift 3) => formUnion - mutable function
[Mutable vs Immutable]
Read more here
Your insert declaration states that the method is returning a tuple: (inserted: Bool, memberAfterInsert: Set.Element), but your method does not return anything.
Just use:
#discardableResult mutating func insert(_ newMembers: [Set.Element]) {
newMembers.forEach { (member) in
self.insert(member)
}
}
UPDATE
The closest to get is this I believe:
extension Set {
#discardableResult mutating func insert(_ newMembers: [Set.Element]) -> [(inserted: Bool, memberAfterInsert: Set.Element)] {
var returnArray: [(inserted: Bool, memberAfterInsert: Set.Element)] = []
newMembers.forEach { (member) in
returnArray.append(self.insert(member))
}
return returnArray
}
}
Reasoning:
The docs to the insert say:
Return Value
(true, newMember) if newMember was not contained in the set. If an element equal to newMember was already contained in the set, the method returns (false, oldMember), where oldMember is the element that was equal to newMember. In some cases, oldMember may be distinguishable from newMember by identity comparison or some other means.
E.g., for set {1, 2, 3} if you try to insert 2, the tuple will return (false, 2), because 2 was already there. The second item of the tuple would be object from the set and not the one you provided - here with Ints it's indistinguishable, since only number 2 is equal to 2, but depending on Equatable implementation you can have two different objects that would be evaluated as the same. In that case the second argument might be important for us.
Anyway, what I am trying to say is, that a single tuple therefore corresponds to a single newMember that you try to insert. If you try to insert multiple new members, you cannot describe that insertion just by using a single tuple - some of those new members might have already been there, thus for the the first argument would be false, some other members might be successfully inserted, thus for them the first argument of tuple would be true.
Therefore I believe the correct way is to return an array of tuples [(inserted: Bool, memberAfterInsert: Set.Element)].
I think what you are looking for is this:
extension Set {
mutating func insert(_ elements: Element...) {
insert(elements)
}
mutating func insert(_ elements: [Element]) {
for element in elements {
insert(element)
}
}
}
The example in your question is breaking a couple of good software programming principals, such as the single responsibility rule. It seems like your function is trying to both modify the current set and return a new set. Thats really confusing. Why would you ever want to do that?
If you are trying to create a new set from multiple sets, then you can do the following:
extension Set {
/// Initializes a set from multiple sets.
/// - Parameter sets: An array of sets.
init(_ sets: Self...) {
self = []
for set in sets {
for element in set {
insert(element)
}
}
}
}

Comparing type of Generic associated type in Swift

I have the next code:
protocol Flyable {
var airspeedVelocity: Double { get }
}
func topSpeed<T: CollectionType where T.Generator.Element == Flyable>(collection: T) -> Double {
return collection.map { $0.airspeedVelocity }.reduce(0) { max($0, $1) }
}
I understood, by reading the Swift Documentation that:
You write a where clause by placing the where keyword immediately after the list of type parameters, followed by constraints for associated types or equality relationships between types and associated types.
This is the example given on the docs:
func allItemsMatch<C1: Container, C2: Container where
C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
(someContainer: C1, _ anotherContainer: C2) -> Bool {
// code
}
Note that when expressing that the associated type ItemType of the C1 type must conform to Equatable protocol you use : and not ==, why is that not the case in my example, where I have to use == to state that the Element associated type of T.Generator must conform with Flyable?
When changing == with : the compiler complains with this:
error: cannot invoke 'topSpeed' with an argument list of type '([Flyable])' note: expected an argument list of type '(T)'
You can achieve this without using Generics.
protocol Flyable {
var airspeedVelocity: Double { get }
}
func topSpeed(collection: [Flyable]) -> Double {
return collection.map { $0.airspeedVelocity }.reduce(0) { max($0, $1) }
}
class Airplane: Flyable {
var airspeedVelocity: Double = 10
}
class Rocket: Flyable {
var airspeedVelocity: Double = 50
}
topSpeed([Airplane(),Rocket()]) //50
I've found the reason here. The where clause has the next grammar:
conformance-requirement → type-identifier­:­type-identifier­
same-type-requirement → type-identifier­==­type­
So, when you want to state that your type parameter conforms to certain protocol you use :. BUT, when you want your type to be exactly of a certain type, you use ==. In my example, I wanted to take as a parameter a collection of Flyable. So, when trying to use : the compiler complained because Flyable doesn't conform to Flyable. Flyable IS Flyable, thus I must use == (or do something like what #Rahul Katariya answered)

Strange generics behavior in swift

Good day!
I am currently trying to learn programming swift and trying some examples of generics. Here is my example code
func findAll<T: Equatable>(arr: [T], _ elem: T) -> [Int] {
var indexesArr = [Int]()
var counter = 0
for i in arr {
if i == elem {
indexesArr.append(counter)
}
counter++
}
return indexesArr
}
findAll([5, 3, 7, 3, 9], 3)
findAll(["a", "b", "c", "b", "d"], 2.0)
It was supposed, that both arguments will be of the same type. But, unfortunately, as you can see I pass an array of string characters and a Double in my second function invocation and it still works! It returns an empty array []. No runtime or compile errors.
Please, explain me why it works and some workarounds maybe. Thank you!
Presumably you have import Cocoa or import Foundation in your code somewhere. If you remove that, you'll find that your code behaves as you're expecting.
The reason this happens is that importing Foundation causes Swift.Array to magically bridge to NSArray (aka [AnyObject]), and certain primitive value types to magically bridge to Foundation object types (like NSString and NSNumber). Once that happens, it's perfectly legal to call your function with parameters of types [AnyObject] and AnyObject.
I'm not aware of a good way around the magic conversion, but one way that works is to force your generic parameter to have a protocol that primitive value types conform to, but primitive values magically wrapped in Cocoa objects don't. One way to assure such is to create your own protocol:
public protocol NotAnyObject {}
extension Int: NotAnyObject {}
extension String: NotAnyObject {}
extension Double: NotAnyObject {}
func findAll<T: Equatable where T: NotAnyObject>(arr: [T], _ elem: T) -> [Int] { /*...*/ }
I'm not sure this is the best way, though... alternative proposals are welcome.
By the way, a more idiomatic/Swifty way to do something like this would be as a type extension (either a protocol extension, as below, or an extension on Array where Element: Equatable). You can even go functional-programming-style just for fun:
extension CollectionType where Generator.Element : Equatable {
public func allIndexesOf(element: Self.Generator.Element) -> [Self.Index] {
return zip(self.indices, self) // makes sequence of (index, element)
.filter { $0.1 == element }
.map { $0.0 }
}
}
Call it like this:
let threes = [5, 3, 7, 3, 9].allIndexesOf(3)
// returns [1, 3]
This is still subject to the same magic conversions issue, though:
let notThrees = ["twenty", "forty", "eight"].allIndexesOf[2.0]
// still compiles, returns []
So you'd still need to either use this extension only from Swift files that don't import Foundation, or apply a variant of the same hack as above, e.g.:
extension CollectionType where Generator.Element: Equatable,
Generator.Element: NotAnyObject { /*...*/ }
For the problem explanation see rickster's answer
Note that to solve problems like this, you can always just check how Swift libraries do it. The solution is to have both arguments as generics and using protocol constraints.
E.g. using SequenceType protocol instead of an Array.
func findAll<S: SequenceType, T: Equatable where S.Generator.Element == T>(arr: S, _ elem: T) -> [Int] {
var indexesArr = [Int]()
var counter = 0
for i in arr {
if i == elem {
indexesArr.append(counter)
}
counter++
}
return indexesArr
}

What should my Array+RemoveObject.Swift contain?

I am using a template project to try to build a new app. When I run the template project, I get an error saying: '[T] does not have a member named 'indexOf'.
The existing code in the Array+RemoveObject.swift doc is:
import Foundation
public func removeObject<T: Equatable>(object: T, inout fromArray array: [T])
{ let index = array.indexOf(object)
if let index = index {
array.removeAtIndex(index)
}
}
Is the problem the use of indexOf? The odd thing is that when I tried using the solution of someone who answered a similar question, I got around 100 errors from the bond framework.
You function on its own works fine for me (Swift 2.1.1, Xcode 7.2). It seems as if you want this function to be a method of a public class of yours. For a minimum working example, you need at least to wrap your removeObject() method within the class you want it to belong to. Note also that you needn't use a separate line for assigning the result from .indexOf(..) call (possibly nil), but can add assignment and nil check in a single if let statement.
public class MyArrayOperations {
// ...
static public func removeObject<T: Equatable>(object: T, inout fromArray array: [T]) {
if let index = array.indexOf(object) {
array.removeAtIndex(index)
}
}
}
var arr = ["1", "2","3"]
MyArrayOperations.removeObject("2", fromArray: &arr)
print(arr) // ["1", "3"]
Also note that you can explicitly state the same behaviour using two generics, in case you want the array itself to conform to some protocol. You then use one separate generic for the array type and one for its elements, thereafter specifying that the element type should conform to the Generator.Element type of the array. E.g.:
func removeObject<T: Equatable, U: _ArrayType where U.Generator.Element == T>(object: T, inout fromArray array: U) {
if let index = array.indexOf(object) {
array.removeAtIndex(index)
}
}
With this approach, you could add an additional protocol at type constraint for the array generic U in the function signature above, e.g.
func removeObject<T: Equatable, U: protocol<_ArrayType, MyProtocol> where U.Generator.Element == T>(object: T, inout fromArray array: [T]) { ...
This can be especially useful when "simulating" generic Array extensions that conforms to some protocol. See e.g. the following for such an example:
extend generic Array<T> to adopt protocol

How do I do indexOfObject or a proper containsObject

With an array: How do I do indexOfObject or a proper containsObject?
I mean I know I could just bridge the Array to NSArray and do it there ^^
But there must be a 'native' way of doing this
P.S. for the containsObject I guess I could filter the array too but for indexOf?
You can use the built-in find, and thus avoid bridging to Objective-C — but only if your element type is Equatable. (If it isn't Equatable, you can make it so with a comparison function and an extension.)
Example:
func == (lhs:Piece,rhs:Piece) -> Bool {
return lhs.val == rhs.val
}
class Piece:Equatable,Printable {
var val : Int
var description : String { return String(val) }
init (_ v:Int) {
val = v
}
}
Now you can call find(arr,p) where arr is an Array<Piece> and p is a Piece.
Once you have this, you can develop utilities based on it. For example, here's a global function to remove an object from an array without bridging to Objective-C:
func removeObject<T:Equatable>(inout arr:Array<T>, object:T) -> T? {
if let found = find(arr,object) {
return arr.removeAtIndex(found)
}
return nil
}
And test it like this:
var arr = [Piece(1), Piece(2), Piece(3)]
removeObject(&arr,Piece(2))
println(arr)
You can do this for NSObject subclasses too. Example:
func == (v1:UIView, v2:UIView) -> Bool {
return v1.isEqual(v2)
}
extension UIView : Equatable {}
Now you can call find on an Array of UIView. It's sort of a pain in the butt, though, having to do this for every single class where you want to be able to use find on an Array of that class. I have filed an enhancement request with Apple requesting that all NSObject subclasses be considered Equatable and that == should fall back on isEqual: automatically.
EDIT Starting in Seed 3, this is automatic for UIView and other NSObject classes. So find now just works for them.
EDIT 2 Starting in Swift 2.0, indexOf will exist as a method:
let s = ["Manny", "Moe", "Jack"]
let ix = s.indexOf("Moe") // 1
Alternatively, it takes a function that returns Bool:
let ix2 = s.indexOf {$0.hasPrefix("J")} // 2
Again, this works only on collections of Equatable, since obviously you cannot locate a needle in a haystack unless you have a way of identifying a needle when you come to it.
EDIT 3 Swift 2.0 also introduces protocol extensions. This means I can rewrite my global function removeObject as a method!
For example:
extension RangeReplaceableCollectionType where Generator.Element : Equatable {
mutating func removeObject(object:Self.Generator.Element) {
if let found = self.indexOf(object) {
self.removeAtIndex(found)
}
}
}
Since Array adopts RangeReplaceableCollectionType, now I can write code like this:
var arr = [Piece(1), Piece(2), Piece(3)]
arr.removeObject(Piece(2))
Oh, happy day!
Its actually able to be done in Swift. To get the index use find(YourArray, ObjectToFind)
As I was told, this isn't available yet / I have to bridge it to NSArray
I don't like this and it feels dirty so I went and did this in an extension. that way it hides the usage of NSArray and allows apple to provide it later
import Foundation
extension Array {
func contains(object:AnyObject!) -> Bool {
if(self.isEmpty) {
return false
}
let array: NSArray = self.bridgeToObjectiveC();
return array.containsObject(object)
}
func indexOf(object:AnyObject!) -> Int? {
var index = NSNotFound
if(!self.isEmpty) {
let array: NSArray = self.bridgeToObjectiveC();
index = array.indexOfObject(object)
}
if(index == NSNotFound) {
return Optional.None;
}
return index
}
//#pragma mark KVC
func getKeyPath(keyPath: String!) -> AnyObject! {
return self.bridgeToObjectiveC().valueForKeyPath(keyPath);
}
}
https://gist.github.com/Daij-Djan/9d1c4b1233b4017f3b67
Apple provide an example of exactly this in the The Swift Programming Language book. Specifically, see the section on Type Constraints in Action (p621 in the iBook).
func findIndex<T: Equatable>(array: [T], valueToFind: T) -> Int? {
for (index, value) in enumerate(array) {
if value == valueToFind {
return index
}
}
return nil
}
Everything depends upon your type implementing Equatable.
The Swift Programming Language covers that and explains how to implement that protocol:
“The Swift standard library defines a protocol called Equatable, which
requires any conforming type to implement the equal to operator (==)
and the not equal to operator (!=) to compare any two values of that
type. ”
NSHipster has a couple of relevant posts on this subject:
Swift Default Protocol Implementations
Swift Comparison Protocols
I also found this answer very useful in implementing Equatable:
How do I implement Swift's Comparable protocol?
Alhough it mentions Comparable, Equatable is a subset and the explanation is good.
Excerpts above from: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/gb/jEUH0.l
One other option is to use filter:
haystack.filter({$0 == needle}).count > 0
This checks to see if array haystack contains object needle.
You can't. That's why NSArray is still there. However, the Apple documentation reads as follows about String and NSString:
Swift’s String type is bridged seamlessly to Foundation’s NSString
class. If you are working with the Foundation framework in Cocoa or
Cocoa Touch, the entire NSString API is available to call on any
String value you create, in addition to the String features described
in this chapter. You can also use a String value with any API that
requires an NSString instance.
Following that approach, the NSArray API should be available on Array, but it isn't because the native Swift Array is a primitive (most likely a struct or similar), so you have to "convert" it to an NSArray.
It appears that not all of the toll-free bridging from NS/CF space is in place. However, if you declare your array as an NSArray, it works fine:
let fruits: NSArray = [ "apple", "orange", "tomato (really?)" ]
let index = fruits.indexOfObject("orange")
println("Index of Orange: \(index)")
If your array elements are objects and you want to find an identical object in that array, you can use this function:
func findObject<C: CollectionType where C.Generator.Element: AnyObject>(domain: C, value: C.Generator.Element) -> Int? {
for (index, element) in enumerate(domain) {
if element === value {
return index
}
}
return nil
}

Resources