How can you check if an object is one of an array of types? - swift

Given the following array:
let ignoredViewControllerTypes:[UIViewController.Type] = [
ViewControllerB.self,
ViewControllerC.self
]
let allViewControllers = [
viewControllerAInstance,
viewControllerBInstance,
viewControllerCInstance,
viewControllerDInstance
]
What is the syntax to filter allViewControllers so that it excludes those types in ignoredViewControllerTypes?
I have tried this, but it doesn't work:
let filteredControllers = allViewControllers.filter{ !ignoredViewControllerTypes.contains($0.self) }
So what am I missing?

This should work:
let filteredControllers = allViewControllers.filter { viewController in
!ignoredViewControllerTypes.contains(where: { type(of: viewController) == $0 })
}

Let's break it down in subtasks:
you want to check if a controller should be allowed or not
func isAllowed(_ controller: UIViewController) -> Bool {
return !ignoredViewControllerTypes.contains { controller.isKind(of: $0) }
}
you want to filter an array of controllers:
let filteredControllers = allViewControllers.filter(isAllowed)
Note that isAllowed also filters subclasses of the ignored controllers, if you want exact type match then you should use #dan's answer.
As a bonus, and because I like functional programming, you can make isAllowed a pure and flexible function by converting it to a high-order function:
func doesntBelong(to prohibitedClasses: [AnyClass]) -> (AnyObject) -> Bool {
return { obj in
prohibitedClasses.contains { obj.isKind(of: $0) }
}
}
, which can be used like this:
let filteredControllers = allViewControllers.filter(doesntBelong(to: ignoredViewControllerTypes))

Related

How to write higher order functions like map, filter, reduce etc.?

I am trying to write or implement my own higher order function my way, but not able to write it.
In below code I tried to write filter.
var array : [String] = ["Sagar","Harshit","Parth","Gunja","Marmik","Sachin","Saurav"]
//Native filter function of Swift
array = array.filter { (name) -> Bool in
return name.prefix(1) == "S"
}
I implement below code, according to method signature of filter, but as I know , we can not write closure with return type(If possible then I don't know).
func filterArray(_ array : [String], completionHandler : (_ name : String) -> ()) -> (){
for (_, value) in array.enumerated(){
completionHandler(value)
}
}
self.filterArray(array) { (name) -> () in
if name.prefix(1) != "S"{
if let index = array.index(of: name){
array.remove(at: index)
}
}
}
My implementation working fine and filtering array. But I want to abstract logic of remove object from array.
Can we write our own higher order functions or not ?
If yes then please help to implement above one.
Thanks in advance.
And you can define a return type to a closure. You can find a working example below, but for this purpose I suggest using the Swift built in filter function which can provide the same solution and much faster.
var array : [String] = ["Sagar","Harshit","Parth","Gunja","Marmik","Sachin","Saurav"]
func filterArray(_ array : inout [String], condition: (_ name : String) -> Bool) -> (){
var filteredArray: [String] = []
for value in array {
if condition(value) {
filteredArray.append(value)
}
}
array = filteredArray
}
filterArray(&array) { (name) -> Bool in
return !name.hasPrefix("S")
}
print(array)
You can define your own higher order functions on collections.
There is a great session about collections where Soroush shows an example of writing your own higher order function extending a collection.
https://academy.realm.io/posts/try-swift-soroush-khanlou-sequence-collection/
// Swit built in filter
let numberOfAdmins = users.filter({ $0.isAdmin }).count // => fine
// Custom "filter"
let numberOfAdmins = users.count({ $0.isAdmin }) // => great
extension Sequence {
func count(_ shouldCount: (Iterator.Element) -> Bool) -> Int {
var count = 0
for element in self {
if shouldCount(element) {
count += 1
}
}
return count
}
}

Calling instance method on each object in array

Let's assume this situation: I have an array of objects and I want call instance method on each one of them. I can do something like that:
//items is an array of objects with instanceMethod() available
items.forEach { $0.instanceMethod() }
The same situation is with map. For example I want to map each object to something else with mappingInstanceMethod which returns value:
let mappedItems = items.map { $0.mappingInstanceMethod() }
Is there a cleaner way to do that?
For example in Java one can do:
items.forEach(Item::instanceMethod);
instead of
items.forEach((item) -> { item.instanceMethod(); });
Is similiar syntax available in Swift?
What you are doing in
items.forEach { $0.instanceMethod() }
let mappedItems = items.map { $0.mappingInstanceMethod() }
is a clean and Swifty way. As explained in Is there a way to reference instance function when calling SequenceType.forEach?, the first statement cannot be reduced
to
items.forEach(Item.instanceMethod)
There is one exception though: It works with init methods
which take a single argument. Example:
let ints = [1, 2, 3]
let strings = ints.map(String.init)
print(strings) // ["1", "2", "3"]
for item in items {
item.instanceMethod()
}
have you tried
let mappedItems = items.map { $0.mappingInstanceMethod() }
note the () to call the method
Edit 1:
sample code:
class SomeObject {
func someFunction() -> Int {
return 5
}
}
let array = [SomeObject(), SomeObject(), SomeObject()]
let ints = array.map { $0.someFunction()}
print(ints)

Cannot convert return expression of type Object<String> to return type Object<Character>

I am working with some piece of code found in Functional programming in Swift, but the book is not updated with Swift 2, and I am getting on error in the following code:
func insert<T: Hashable>(key: [T], trie: Trie<T>) -> Trie<T> {
if let (head, tail) = key.decompose {
if let nextTrie = trie.children[head] {
var newChildren = trie.children
newChildren[head] = insert(tail, trie: nextTrie)
return Trie(isElem: trie.isElem, children: newChildren)
} else {
var newChildren = trie.children
newChildren[head] = single(tail)
return Trie(isElem: trie.isElem, children: newChildren)
}
} else {
return Trie(isElem: true, children: trie.children)
}
}
func buildString(words: [String]) -> Trie<Character> {
return reduce(words, empty()) { trie, word in
insert([word], trie: trie)
}
}
Inside buildString I get this:
Cannot convert return expression of type Trie<String> to return type Trie<Character>
How can I fix this ?
Assuming that empty looks like this
func empty<T>() -> Trie<T> {
return Trie<T>()
}
change the buildString function to
func buildString(words: [String]) -> Trie<Character> {
return words.reduce(empty()) { trie, word in
insert(Array(word.characters), trie: trie)
}
}
Explanation
What you want in the first argument of insert is an array of Character instances which you get by calling Array(word.characters). Array(word) is the Swift 1.2 way and doesn't work in Swift 2 anymore. In addition, [word.characters] just creates an array with a single element (the string's character view) which is why it doesn't work as well.
Note that I also changed the global reduce function (Swift 1.2 way) to the instance method .reduce (Swift 2 way).

Array transform having failable initialiser

I am using Swift 1.2 in Xcode 6.3.1
Following is my Person struct
struct Person {
let age: Int
init?(age: Int) { //Failable init
if age > 100 { return nil }
self.age = age
}
}
I am having a list of ages against which I have to make Person Objects.
I have made playground file.
let arr = Array(1...150) //Sample set of ages against which Person is created
var personList: [Person]!
and
personList = arr.map({ (val: Int) -> Person? in
return Person(age: val) //Makes object of type Person?
}).filter {
$0 != nil
}.map {
return $0!
}
Here I have uses map - filter - map because the first map invokes failable intializer, (hence it returns Person?) and personList is of type [Person].
Hence second function filters all the non nil objects and third map forcefully opens to optional therby making Person? to Person.
Is there a more easy/readable way out ? Chaining map-filter-map definitely seems to be an overkill for this
You can use flatMap to get rid of any nils in the array, this tutorial discusses the method in length, but the following will work best:
let personList = arr.flatMap { Person(age: $0) }
Note: This answer was given for Swift 1.2, the current
version at the time the question was posted. Since Swift 2 there is a better solution, see #Jeremie's answer.
I don't know of a built-in function that combines filter()
and map(). You can write the code slightly more compact using
the shorthand argument $0 in all closures:
let personList = arr.map { Person(age: $0) }
.filter { $0 != nil }
.map { $0! }
Of course you can define your own extension method which maps the
array elements and keeps only the non-nil results:
extension Array {
func optmap<U>(transform: T -> U?) -> [U] {
var result : [U] = []
for elem in self {
if let mapped = transform(elem) {
result.append(mapped)
}
}
return result
}
}
and then use it as
let personList = arr.optmap { Person(age: $0) }
You can use compactMap which is better that flatMap in this case to remove any nils in the array:
let personList = arr.compactMap { Person(age: $0) }
The Swift document declared:
Returns an array containing the non-nil results of calling the given
transformation with each element of this sequence.

How to compare nested collections in swift

I have two collections:
let collection1:[String:[String:NSObject]] = ["somekey":["nestedkey":"value"]]
let collection2:[String:[String:NSObject]] = ["somekey":["nestedkey":"value"]]
//I would like to compare them using the following:
let collectionsAreEqual = collection1 == collection2
Copying and pasting the above code into a playground gives the following error:
I know I can write an equal function for this:
infix func == (this:[String:[String:NSObject]], that:[String:[String:NSObject]]){
//return true or false
}
In objective c, isEqual: on an NSDictionary handles this no problem, because it does the nested comparison for you. Is there some method of generally handling this in swift?
Update
I can use the following:
//:[String:[String:NSObject]]
let collection1:[String:NSObject] = ["somekey":["nestedkey":"value"]]
let collection2:[String:NSObject] = ["somekey":["nestedkey":"value"]]
//I would like to compare them using the following:
let collectionsAreEqual = collection1 == collection2
but it requires using NSObject as the value in the declaration. Is there a pure swift method to handle this?
Here's an equality operator that will compare any two nested dictionaries with the same type:
func ==<T: Equatable, K1: Hashable, K2: Hashable>(lhs: [K1: [K2: T]], rhs: [K1: [K2: T]]) -> Bool {
if lhs.count != rhs.count { return false }
for (key, lhsub) in lhs {
if let rhsub = rhs[key] {
if lhsub != rhsub {
return false
}
} else {
return false
}
}
return true
}
try this:
let collection1:[String:NSObject] = ["somekey":["nestedkey":"value"]]
let collection2:[String:NSObject] = ["somekey":["nestedkey":"value"]]
let collectionsAreEqual = ((collection1 as NSDictionary).isEqual(collection2 as NSDictionary)
By casting the swift objects to Foundation objects they get the equality operator. They will call the equality operator on every element recursively, so there you have your deep comparison.