map high order function format in swift - swift

I am wondering why map format has to be {( )} rather than just { }
func intersect(_ nums1: [Int], _ nums2: [Int]) -> [Int] {
// the following is right
var num1Reduce = nums1.reduce(0){ $0 + $ 1}
/// the following is wrong ??
var num2Dict = Dictionary(nums2.map{ $0, 1 }, uniquingKeysWith : +)
// the following is right
var num1Dict = Dictionary(nums1.map{ ($0, 1) }, uniquingKeysWith : +)
}
and I even see the following format ({ }). I am totally confused!
let cars = peopleArray.map({ $0.cars })
print(cars)

You are using the following Dictionary initializer:
init<S>(_ keysAndValues: S, uniquingKeysWith combine: (Dictionary<Key, Value>.Value, Dictionary<Key, Value>.Value) throws -> Dictionary<Key, Value>.Value) rethrows where S : Sequence, S.Element == (Key, Value)
Note that S is a sequence where its elements are a tuple of key/value pairs.
When you pass nums1.map{ ($0, 1) } to the first parameter, you are creating an array of key/value tuples from nums1.
It fails when you use nums2.map{ $0, 1 } because that is missing the parentheses for the tuple.
Keep in mind that nums1.map{ ($0, 1) } is shorthand for nums1.map({ ($0, 1) }). That's all related to trailing closures which has nothing to do with the parentheses for the tuple that appear inside the { }.

A map is a function that takes a closure as a parameter. We can call the map and pass the parameter like we do for any other ordinary function call without removing the brackets ()e.g
(0...100).map ({ _ in print("yeti")})
But swift allows us to remove the brackets as a way of shorthanding and we can write it like, hence eliminating the ()
(0...100).map { _ in print("yeti")}
But incase you want to access individual values of the array elements, you can do so in two ways,
Given an array, you can access it's individual element using $0, which basically says, Hey map, give me the first element at this current index.
(0...100).map {$0}
Instead of using the default swift indexing, you decide to define the value you are accessing by giving it a readable variable name e.g
(0...100).map {element in}
This gets $0 and assigns it to element, the in keyword basically tells the compiler that hey, $0 is now element and we are going to use it after in. Otherwise if you remove the in keyword, the compiler says it doesn't know any variable called element.
For special collections like dictionaries, they have two values per index, i.e the key and value, therefore if you want to access the contents of a dictionary during the mapping, you can do it in two ways like above, a). use the default swift indexes, or give the values per index, readable variable names. e.g
let dictionary = ["a": 3, "b": 4, "c": 5]
dictionary.map{($0, $1)}
We use inner brackets () to let the compiler know that the collection we are mapping over has two values per index. Please note the inner parenthesis are creating a tuple
dictionary.map {(key, value) in }

Related

Swift: Dictionary of Dicitionaries, cannot get subscript

I've looked at other subscript issues here and I don't think they match my problem. I have a dictionary of dictionaries - Dictionary[String:Dictionary[String:String]]
In an extension I want to loop through all the values (Dictionary[String:String] and retrieve one of the values.
So I wrote this:
for dictNEO in Array(self.values) {
print(dictNEO)
print(type(of: dictNEO))
print(dictNEO["approachDate"])
}
and am getting this error on the last print line: Value of type 'Value' has no subscripts
Here's the first two print lines:
["nominalDist": "\"13.58 ", "approachDate": "\"2020-Feb-01 08:18 ± < 00:01\"", "minimumDist": "\"13.58 ", "diameter": "\"92 m - 210 m\"", "name": "\"(2017 AE5)\""]
Dictionary<String, String>
So I am confused as to why it is telling me it has no subscripts when it sees the type of as a Dictionary.
You have written this as an extension to Dictionary if I understand you correctly and that means that self is generic and defined as Dictionary<Key, Value> and not to you specific type so in your for loop you are looping over an array of [Value].
So you need to typecast Value before accessing it as a dictionary
if let dictionary = dictNEO as? [String: String] {
print(dictNEO["approachDate"])
}
but since it makes little sense to have an extension to Dictionary where you access a specific key it would be better to write it as a function. Since the dictionary is well defined now there is no issue with the last print
func printValuesForSubKey(_ key: String, _ dict: [String: [String: String]]) {
for (dictNEO) in dict.values {
print(dictNEO)
print(type(of: dictNEO))
print(dictNEO[key])
}
}
Note, I don't have an explanation why type(of:) recognises it as [String: String]
The code snippet doesn't work because values property is a collection of collections and with Array(values) you create a collection of collection of collections. In short, instead going down the code goes up and creates new collection level.
Solution with a Higher order function map:
self.values.map { print(type(of: $0)); $0["approachDate"] }
Solution with For-In Loop
for dictNEO in self.values {
print(dictNEO)
print(type(of: dictNEO))
print(dictNEO["approachDate"])
}

Swift array reduction: cannot use mutating member on immutable value

I have the following code that attempts to consolidate redundant elements of an array:
var items : [String] = ["hello", "world", "!", "hello"]
var mutableSet = Set<String>()
items.reduce(mutableSet, combine: { (set: Set<String>, element: String) in
return set.insert(element)
})
set.insert(element) gives me the error Cannot use mutating member on immutable value: 'set' is a 'let' constant. What's wrong and how can I fix it?
The problem with the OP's code is that the accumulator in the reduce is immutable so it won't allow you to use the mutating function insert().
A tidy solution is to define an non-mutating equivalent to insert() called inserting() in an extension to Set as follows.
extension Set {
//returns a new set with the element inserted
func inserting(_ element: Element) -> Set<Element> {
var set = self
set.insert(element)
return set
}
}
Now we can write the reduce as follows
var items : [String] = ["hello", "world", "!", "hello"]
let set = items.reduce(Set<String>()){ accumulator, element in
accumulator.inserting(element)
}
In Swift, collections are value types. Value-typed variables declared with let (as implicitly are function parameters) cannot be modified. Additionally, your closure returns nothing, so reduce will probably not succeed.
I believe that reduce is not the best-suited tool for this task. Consider this for loop instead:
var set = Set<String>()
for element in items { set.insert(element) }
Another even simpler option would be to use the unionInPlace method:
var set = Set<String>()
set.unionInPlace(items)
Even better perhaps, create the set straight from the collection:
var set = Set<String>(items)
The 'set' value returned is a constant. This is important as it is the accumulator, which represents the values that have accumulated, thus far. It should not change in your closure.
Here is an example from a project I'm working on at the moment, where I want to find all of the unique performers, across many theatrical performances. Notice how I am using union, which does not modify the constant value 'performers', but instead consumes it to produce a new value.
let uniquePerformers = performances.reduce(Set<Performer>(), { (performers: Set<Performer>, performance) -> Set<Performer> in
return performers.union(Set(performance.performers))
})

How do you use the map function in Swift?

I'm trying to understand how .map works and all of my searches are returning examples that aren't helping me resolve my issue. From my understanding .map is a cleaner way of performing a for-in loop, the only difference is that .map replaces your original creates a new array and for-in just alters your original arrays values.
Attempt
In the code example below, I'm expecting the .map function to replace my wheels array with new Tire objects whose state properties are all set to .Flat.
This is a playground-ready version of what I'm trying to accomplish in my program:
enum State {
case Ok, Flat
}
class Tire {
var state = State.Ok
init(t: State) {
state = t
}
}
var wheels = [Tire(t: .Ok), Tire(t: .Ok), Tire(t: .Ok)]
wheels = wheels.map { $0.state = .Flat }
Result
error: cannot convert value of type '()' to closure result type 'Tire'
wheels = wheels.map { $0.state = .Flat }
~~~~~~~~~^~~~~~~
Question
In the given situation, how can I set all of my Tire objects states to .Flat using .map?
There are 2 similar key functions which perform similar operations, the basic purpose of which is to take an array and build another array from it:
func map(transform:(R)->T) -> [T] --- Map takes an array of elements of one type and converts it to an array of elements of (potentially) another type, by calling a transform function on each element in turn. So you can convert an array of Int's to an array of strings:
[1, 2, 3, 4].map { "\($0)" } // --> ["1", "2", "3", "4"]
func filter(predicate:(T)->Boolean) -> [T] -- Filter takes an array of elements and converts it to an array of elements of the same type, but only includes those elements for which predicate returns true. So you can filter an array of ints to leave only the even numbers:
[1, 2, 3, 4].filter { $0 % 2 == 0 } // --> [ 2, 4]
There are other variants, such as flatMap which takes [[T]] and turns it into [T] by iterating over the input and array and appending the contents of each array to an output array:
[ [1, 2], [3, 4]].flatMap() // --> [1, 2, 3, 4]
It's also worth nothing that the concept behind map is that, in simplistic terms, it can be used to map any input type to an output type, so you can define:
func <R, T> map(in:R?, transform:(R)->T) -> T?
for example, which would translate any optional input type into an optional output type given a function that translates the base type.
The problem is $0.state = .Flat is an assignment. It does not return a value. Try this:
wheels = wheels.map { w in
w.state = .Flat
return w
}
map does not replace anything. It projects each element from your array to a new array by applying the transformation block. You can choose to assign this new array to the old array, but otherwise it will not alter the original array.

find() using Functional Programming

I'd like to create a generic find() typically used in functional programming. In functional programming you don't work with array indices and for loops. You filter. The way it works is that if you have a list of say
["apple", "banana", "cherry"]
and you want to find banana then you assign the array indices to the list elements by creating tuples
[(1, "apple"), (2, "banana"), (3, "cherry")]
Now you can filter down to "banana" and return the index value.
I was trying to create a generic function for this but I get an error. What's wrong with this syntax?
func findInGenericIndexedList<T>(indexedList: [(index: Int, value: T)], element: (index: Int, value: T)) -> Int? {
let found = indexedList.filter { // ERROR: Cannot invoke 'filter' with an argument list of type '((_) -> _)'
element.value === $0.value
}
if let definiteFound = found.first {
return definiteFound.index
}
return nil
}
UPDATE 1: I would like to use the above solution as opposed to using find() (will be deprecated) or in Swift 2.0 indexOf() because I'm trying to follow the Functional Programming paradigm, relying on general functions and not class methods.
The minimum change required to make this work would be to make T conform to Equatable and use the == operator.
func findInGenericIndexedList<T:Equatable>(indexedList: [(index: Int, value: T)], element: (index: Int, value: T)) -> Int? {
let found = indexedList.filter {
element.value == $0.value
}
if let definiteFound = found.first {
return definiteFound.index
}
return nil
}
It doesn't really make sense to use === here because are usually going to be applying this to value types (especially if you are following functional paradigms) for which this is never true.
Beyond this I spent some time thinking about the problem and here is what I would do:
extension Array where Element : Equatable {
func find(element:Array.Generator.Element) -> Int? {
let indexedList = lazy(self.enumerate())
let found = indexedList.filter {
element == $1
}
let definiteFound = found.prefix(1)
return definiteFound.generate().next()?.index
}
}
Protocol extension on Array because it makes the syntax neater, lazy sequence to avoid checking every element, 0 indexed.
Here are a couple of thoughts.
I would still prefer to use the identity operator '===', because in my array I may have multiple items of the same value.
The identity operator === only works for reference types, like classes. It will never work for value types, like Strings or Ints, or structs, etc. You might take a look at the difference between value types and reference types, especially if you are interested in functional programming, which eschews reference types almost completely. When you are working with value types, there is only equality (==) - there is no identity. Two instances of the String "bananas" will never refer to the same identical object. They will always refer to two different Strings, though their values might be equal.
I want to delete the exact item that I passed to the function. Is it impossible to do this?
If you are working with value types, like Strings, then yes, it is impossible. There is no such thing as two different Strings that are the exact same item. Two Strings are always are always different objects, for the reasons stated above.
Note that if you work only with classes, and not value types, then you could use the === operator, but this would defeat much of what you are trying to do.
What this boils down to is that if you have an array of (index, value) tuples that looks like this:
[(0, "bananas"), (1, "apples"), (2, "oranges"), (3, "bananas")]
And you write a function that looks for tuples where the value is "bananas", you have a couple of choices. You can filter it and look for the first tuple in the array that has the value "bananas" and return the index of that tuple. In the above case it would return 0. Or, you could return all of the indexes in the form of an array, like this: [0, 3]. Or I suppose you could return some other arbitrary subset of the results, like the last index, or the first-and-last indexes, etc., but those all seem a little silly. The Swift standard library opts for returning the index of the first item that matches the search criteria for precisely this reason. None of the other options make a whole lot of sense.
But putting it back into the context of your question, none of the tuples that you find with the value of "bananas" are going to be the exact (identical) instance of "bananas" that you passed in to your search function. No two value types are ever identical. They may be equal, but they are never identical.
One more note - mainly for clarification of what you are even trying to do. In your first attempt at writing this function, you appear to already know the index of the item you are searching for. You pass it in to the function as a parameter, right here:
// ----------vvvvv
func findInGenericIndexedList<T>(indexedList: [(index: Int, value: T)], element: (index: Int, value: T)) -> Int?
Just out of curiosity, is this a typo? Or do you actually know the index of the tuple that you are searching for? Because if you already know what it is, well... you don't need to search for it :)

How to correctly use short Swift closures in calls to ObjC classes

I want to use short form of closure {$0 > 1} in calls to NSIndexSet class:
let indexSet: NSIndexSet = getSomeIndexSet()
let filteredIndexSet = indexSet.indexesPassingTest(){$0 > 1}
but it gives me
Cannot invoke 'indexesPassingTest' with an argument list of type '((_) -> _)'
but this works: indexSet.indexesPassingTest(){(i,s) in i > 1} though type names are still not there.
Is it a bug or am I missing something?
The error message says what you are doing wrong. The argument passed to block are not two different argument rather a single argument which is tuple. So, you will have to access each element in from the tuple.
Based on the comment by Martin R, it seems like closure must match the 2 arguments. So, one can use $0, or $1 or if only one is used then $0 becomes tuple.
let filteredIndexSet = indexSet.indexesPassingTest { $0.0 > 20 }
The $0.0 means the first item in the tuple which is index.