What is the purpose of .enumerated() and .map() in this code? - swift

I'm working a tutorial from https://www.raywenderlich.com/921-cocoa-bindings-on-macos. I'm wondering what the .enumerated() and .map() functions are operating on in this section:
#IBAction func searchClicked(_ sender: Any) {
guard let resultsNumber = Int(numberResultsComboBox.stringValue)
else {
return
}
iTunesRequestManager.getSearchResults(searchTextField.stringValue, results: resultsNumber, langString: "en_us") { (results, error) in
let itunesResults = results.map {
return Result(dictionary: $0)
}
.enumerated()
.map({ (index, element) -> Result in
element.rank = index + 1
return element
})
DispatchQueue.main.async {
self.searchResultsController.content = itunesResults
print(self.searchResultsController.content!)
}
}
}
I can usually figure out most things eventually in Swift but I'm stumped here and the explanatory text isn't clear to me either. I hope someone can help me understand this part of the tutorial. Thanks!

Map is used for modifications. At this point you are basically initialising an object of Result by giving results array as a param to it:
results.map {
return Result(dictionary: $0)
}
$0 means the first input. In a following case, $0 is equal to param(we just gave it a name):
results.map { param in
return Result(dictionary: param)
}
.enumerated() returns each element of an array with its index number. Without it you would have only the element like this:
.map({ (element) -> Result in
// you don't have `index` value inside the closure anymore
// element.rank = index + 1
return element
})
Note that the element in the closure above is the same Result(dictionary: $0) object that you created in a previous map function.
At the end, you are making and modification by assigning elements index number increased by 1 to the element's rank property, and returning it:
.map({ (index, element) -> Result in
// element.rank = index + 1
return element
})
Note that the value we get after the 3rd step, including all modification is assigned to let itunesResults.

Related

Get next value on a map?

I'm trying to compare element to next element in a collection.
For example :
let array: [(Double, String)]= [(2.3, "ok"),
(1.4, "ok"),
(5.1, "notOk")]
I need a returned array who will summary element where the string is the same. So my result will be :
new array = [(3.7, "ok"), (5.1, "notOk")]
I need to do it functional if possible. i tried to get next element in a map but can't found how.
Something like this (this is just for logic, this code isn't working.
let newArray = array.map {(element, nextElement) in
if element.1 == nextElement.1 {
return element.0 + nextElement.0
}
}
In a more functional way:
let array: [(Double, String)]= [(2.3, "ok"),
(1.4, "ok"),
(5.1, "notOk")]
let keys = Set(array.map{$0.1}) // find unique keys
let result = keys.map { key -> (Double, String) in
let sum = array.filter {$0.1 == key} // find all entries with the current key
.map {$0.0} // map them to their values
.reduce(0, +) // sum the values
return (sum, key)
}
print(result)
Output:
[(5.0999999999999996, "notOk"), (3.6999999999999997, "ok")]
Alternatively (suggested by #dfri):
let keys = Set(array.map{$0.1}) // find unique keys
let result = keys.map { key -> (Double, String) in
let sum = array.reduce(0) { $0 + ($1.1 == key ? $1.0 : 0) }
return (sum, key)
}
I like alexburtnik's answer. It's basically word for word how I wrote my first pass of this. It's straightforward, clear, and efficient. It is excellent Swift.
But functional programming can help us think more deeply about problems and create better, reusable tools. So let's think functionally.
dfri's solution appears beautiful, but is O(m*n) (in the worst case, O(n^2)). It loops through the entire array for every unique key. This gets back the old adage by Alan Perlis: "A Lisp programmer knows the value of everything and the cost of nothing." But functional programming doesn't have to be inefficient.
The point of functional programming is to break down complex problems into simpler problems, make those simpler problems generic, and then recombine them. It's not about filters and flatMaps.
So let's break down this problem. We want to group by key, and then sum the values for each key. Grouping by key is going to be a lot easier if we sort by key first:
let result = array
.sorted(by: { $0.1 < $1.1 })
Now, we wish we could group them with something like this:
let result = array
.sorted(by: { $0.1 < $1.1 })
.grouped(by: { $0.1 == $1.1 })
I wish I had that grouped(by:). Wish fulfillment is the heart of functional programming, so let's write it. Well, a group is a sequence of elements that are all "equal" for some value of "equal." We could build that this way:
extension Array {
func grouped(by equal: (Element, Element) -> Bool) -> [[Element]] {
guard let firstElement = first else { return [] }
guard let splitIndex = index(where: { !equal($0, firstElement) } ) else { return [self] }
return [Array(prefix(upTo: splitIndex))] + Array(suffix(from: splitIndex)).grouped(by: equal)
}
That said, I don't really like this code. It's not very Swifty. That [Array(prefix(...)] + is a good indication of how much Swift hates us doing it this way. And it can be very expensive due to copying (probably getting us back to O(n^2). The Swiftier solution would be an Sequence:
struct GroupedSequence<Element>: Sequence, IteratorProtocol {
var elements: [Element]
let equal: (Element, Element) -> Bool
private var nextIndex = 0
init(of elements: [Element], by equal: #escaping (Element, Element) -> Bool) {
self.elements = elements
self.equal = equal
}
mutating func next() -> ArraySlice<Element>? {
guard nextIndex < elements.endIndex else { return nil }
let first = elements[nextIndex]
let splitIndex = elements[nextIndex..<elements.endIndex].index(where: { !equal($0, first) } ) ?? elements.endIndex
defer { nextIndex = splitIndex }
return elements[nextIndex..<splitIndex]
}
}
extension Array {
func grouped(by equal: #escaping (Element, Element) -> Bool) -> GroupedSequence<Element> {
return GroupedSequence(elements: self, equal: equal)
}
}
Yes, it mutates and it's a little more code, but it's also lazy (which is a key tool from functional programming), it's better Swift, and very reusable. I like it. But you can use the recursive, pure version if you like.
OK, so now we have an array of arrays that are equivalent. We want to map over those and reduce each element to its sum. So we'll have a reduce inside a map. But this is not O(n^2) because each reduce is only over a single slice. We're going to walk every element just one time. To take care of one impossible corner case (an empty group, which grouped(by:) will never actually create), we'll use flatMap, but it's really just a map. You might be tempted to jump to this, but don't do it:
let result: [(Double, String)] = array
.sorted(by: { $0.1 < $1.1 })
.grouped(by: { $0.1 == $1.1 })
.flatMap { group in
guard let key = group.first?.1 else { return nil }
return (group.reduce(0, { $0 + $1.0 }), // Sum of our values
key)
}
Why? That's horribly unreadable. This is what gives functional programming a bad name. What the heck is that last piece doing? No, we want functional composition, not just functional tools. So we extract a function:
func sumEach(pairGroup: ArraySlice<(Double, String)>) -> (Double, String)? {
guard let key = pairGroup.first?.1 else { return nil }
return (pairGroup.reduce(0, { $0 + $1.0 }), // Sum of our values
key)
}
Now, we can have our nice functional approach without sacrificing comprehension:
let result = array
.sorted(by: { $0.1 < $1.1 })
.grouped(by: { $0.1 == $1.1 })
.flatMap(sumEach(pairGroup:))
And in the process we've created a new tool, grouping, that we can use to compose other solutions. I think that's pretty nice.
But I'd still probably do it alexburtnik's way.
You can iterate over every tupple in your input array and save a sum in a dictionary like this:
let array: [(Double, String)] = [(1.0,"notok"),(2.0,"ok"),(3.0,"ok"),(4.0,"ok"),(5.0,"ok"),(6.0,"ok"), (7.0,"notok")]
var dict = [String: Double]()
for (value, key) in array {
dict[key] = (dict[key] ?? 0) + value
}
print ("dict: \(dict)")
Output:
dict: ["notok": 8.0, "ok": 20.0]
If you really need to get an array of tuples, use this:
let result = dict.map { (key, value) in (value, key) }
print ("result: \(result)")
Output:
result: [(8.0, "notok"), (20.0, "ok")]
I guess that a solution that makes a good use of Swift's features would be to combine filter and reduce:
let array: [(String, Double)] = [("ok", 2.4),
("ok", 1.3),
("not ok", 4.4),
("very not ok", 99.0)]
let key = "ok"
let result = array.filter({$0.0 != key}) + [array.filter({ $0.0 == key }).reduce((key, 0.0), { (key, $0.1 + $1.1) })]
print(result)
And then the result would be
[("not ok", 4.4000000000000004), ("very not ok", 99.0), ("ok", 3.7000000000000002)]
Which I assume is what you wanted to achieve.
EDIT:
To reduce all tuples you could simply wrap the solution inside of a function:
func reduceAllTuples(tupleArray: [(String, Double)]) -> [(String, Double)]{
var array = tupleArray
for (key, _) in tupleArray {
array = array.filter({$0.0 != key}) + [array.filter({ $0.0 == key }).reduce((key, 0.0), { (key, $0.1 + $1.1) })]
}
return array
}

Why does map(_:) in Swift Playground return a String and not a tuple?

I am attempting to use Swift Playground to use map(_:) and enumerated() to walk through an array of orders, returning the first perfect match to a customers goods.
However, when testing in Swift Playground; the map(_:) function returns a string when it should be a tuple.
I'm attempting to retrieve the index and the value; of a given array filter.
Right now, my current solution is this;
let orders = [4,2,7]
let goods = 2
var matching:Int = (orders.filter{ $0 == goods }.first) ?? 0 as Int
In this example, the answer is 2; however it doesn't give me the index of the array.
My second attempt in Swift Playground is thus
var r = (orders.filter{ $0 == goods }).enumerated().map { (index, element) -> (Int,Int) in
return (index, element)
}
print (r.first!) // This should report (0,2)
However, this in Swift Playground prints out in the sidebar panel
"(0, 2)\n"
Screenshot:
Why does the sidebar reporting that this is a string?
Is there a way to get the index and element correctly in this example?
Get Index of Orders that matches Goods
To get index & element:
var r = orders.enumerated().map { ( index, element) -> (Int, Int) in
return (index, element)
}.filter { (index, element) -> Bool in
if element == goods {
return true
}
return false
}
or more compact:
var r = orders.enumerated().map { index, element in (index, element) }
.filter { _, element in element == goods ? true : false }
print("r: \(r.first)")
Prints:
r: (1, 2)
If you really want to find the first match only, a for loop is more efficient, as you can break after the first match is found.
Playground
What you see "(0, 2)\n" is the result of print. What it prints out in console is (0, 2) plus newline.
If you want to see the actual value of r.first!in the sidebar, remove the print:
print (r.first!)
r.first!
Result:
Others have already covered the answer, this is just a side note. I suggest you break down your statement to make it clearer.
var r = orders.filter{ $0 == goods }
.enumerated()
.map{ index, element in (index, element) }
The print statement puts your output in "" and also adds the linebreak \n at the end.
If you write r.first!, you will see that it actually is a tuple.

swift generics return first and last element

I'm trying to get used to generics (never used them in objc) and want to write a toy function that takes an object of any type () and returns the first and last element. Hypothetically, I'd only use this on an array or a string - I keep getting an error that has no subscript members. I totally understand that the error message is telling me swift has no clue that T may potentially hold a type that does have subscripts - I just want to know how to get around this.
func firstAndLastFromCollection<T>(a:T?) {
var count: Int = 0
for item in a as! [AnyObject] {
count++
}
if count>1 {
var first = a?[0]
var last = a?[count-1]
return (first, last)
}
return something else here
}
Do I need to typecast somewhere here (which would kind of defeat the purpose here, as I'd need to downcast as either a string or an array, adding code and lessening how generic this func is)?
If you want to return the first and the last element then it's probably safe assuming the input param is an array of some kind of type.
So you can implement your function this way
func firstAndLast<T>(list:[T]) -> (first:T, last:T)? {
guard let first = list.first, last = list.last else { return nil }
return (first, last)
}
The function does return a tuple of 2 element, both have the same type of the generic element of the input array.
The returned tuple is an option because if the array is empty then nil is returned.
Examples
let nums = firstAndLast([1,2,3,4])
let words = firstAndLast(["One", "Two", "Three"])
As you can verify the type of the generic element into the array becomes the type of the elements inside the tuple.
In the example above nums is inferred to be (Int, Int)? and words (Words, Words)?
More examples
let emptyList: [String] = []
firstAndLast(emptyList) // nil
Extension
Finally you can also write this code as an extension of Array.
extension Array {
var firstAndLast: (first:Element, last:Element)? {
guard let first = self.first, last = self.last else { return nil }
return (first, last)
}
}
Now you can write
let aCoupleOfShows = ["Breaking Bad", "Better Call Saul", "Mr Robot"].firstAndLast
Again, if you check the type of the constant aCoupleOfShows you'll see that is a (first: String, last: String)?. Swift automatically did infer the correct type.
Last example
In the comments you said you wanted the first and last chars of a String. here it is the code if you use the extension above
if let chars = Array("Hello world".characters).firstAndLast {
print("First char is \(chars.first), last char is \(chars.last) ")
}
//>> First char is H, last char is d
If we are talking about collections, let's use the CollectionType:
func firstAndLastFromCollection<T: CollectionType>(a: T) -> (T.Generator.Element, T.Generator.Element)? {
guard !a.isEmpty else {
return nil
}
return (a.first!, a.lazy.reverse().first!)
}
print(firstAndLastFromCollection(["a", "b", "c"])) // ("a", "c")
print(firstAndLastFromCollection("abc".characters)) // ("a", "c")
print(firstAndLastFromCollection(0..<200)) // (0, 199)
print(firstAndLastFromCollection([] as [String])) // nil
If you specify your generic type to also conform to bidirectional index:
func firstAndLastFromCollection<T: CollectionType where T.Index : BidirectionalIndexType>(...) -> ...
then you can call last directly:
return (a.first!, a.last!)
If we decide to implement it using a category, we don't need generics at all:
extension CollectionType {
func firstAndLast() -> (Generator.Element, Generator.Element)? {
guard !self.isEmpty else {
return nil
}
return (self.first!, self.lazy.reverse().first!)
}
}
extension CollectionType where Index: BidirectionalIndexType {
func firstAndLast() -> (Generator.Element, Generator.Element)? {
guard !self.isEmpty else {
return nil
}
return (self.first!, self.last!)
}
}
print("abc".characters.firstAndLast())
Swift is a protocol oriented language. Usually you will find yourself extend protocols more than extending classes or structs.

Unwrapping optional inside of closure using reduce

I have a quick question that is confusing me a little bit. I made a simple average function that takes an array of optional Ints. I check to make sure the array does not contain a nil value but when I use reduce I have to force unwrap one of the two elements in the closure. Why is it that I only force unwrap the second one (in my case $1!)
func average2(array: [Int?]) -> Double? {
let N = Double(array.count)
guard N > 0 && !array.contains({$0 == nil}) else {
return nil
}
let sum = Double(array.reduce(0) {$0+$1!})
let average = sum / N
return average
}
I know it is simple but I would like to understand it properly.
The first parameter of reduce is the sum, which is 0 in the beginning. The second one is the current element of your array which is an optional Int and therefore has to be unwrapped.
Your invocation of reduce does this:
var sum = 0 // Your starting value (an Int)
for elem in array {
sum = sum + elem! // This is the $0 + $1!
}
EDIT: I couldn't get a more functional approach than this to work:
func average(array: [Int?]) -> Double? {
guard !array.isEmpty else { return nil }
let nonNilArray = array.flatMap{ $0 }
guard nonNilArray.count == array.count else { return nil }
return Double(nonNilArray.reduce(0, combine: +)) / Double(nonNilArray.count)
}
You can also discard the second guard if you want something like average([1, 2, nil]) to return 1.5 instead of nil

Swift Array - Check if an index exists

In Swift, is there any way to check if an index exists in an array without a fatal error being thrown?
I was hoping I could do something like this:
let arr: [String] = ["foo", "bar"]
let str: String? = arr[1]
if let str2 = arr[2] as String? {
// this wouldn't run
println(str2)
} else {
// this would be run
}
But I get
fatal error: Array index out of range
An elegant way in Swift:
let isIndexValid = array.indices.contains(index)
Type extension:
extension Collection {
subscript(optional i: Index) -> Iterator.Element? {
return self.indices.contains(i) ? self[i] : nil
}
}
Using this you get an optional value back when adding the keyword optional to your index which means your program doesn't crash even if the index is out of range. In your example:
let arr = ["foo", "bar"]
let str1 = arr[optional: 1] // --> str1 is now Optional("bar")
if let str2 = arr[optional: 2] {
print(str2) // --> this still wouldn't run
} else {
print("No string found at that index") // --> this would be printed
}
Just check if the index is less than the array size:
if 2 < arr.count {
...
} else {
...
}
Add some extension sugar:
extension Collection {
subscript(safe index: Index) -> Iterator.Element? {
guard indices.contains(index) else { return nil }
return self[index]
}
}
if let item = ["a", "b", "c", "d"][safe: 3] { print(item) } // Output: "d"
// or with guard:
guard let anotherItem = ["a", "b", "c", "d"][safe: 3] else { return }
print(anotherItem) // "d"
Enhances readability when doing if let style coding in conjunction with arrays
the best way.
let reqIndex = array.indices.contains(index)
print(reqIndex)
Swift 4 extension:
For me i prefer like method.
// MARK: - Extension Collection
extension Collection {
/// Get at index object
///
/// - Parameter index: Index of object
/// - Returns: Element at index or nil
func get(at index: Index) -> Iterator.Element? {
return self.indices.contains(index) ? self[index] : nil
}
}
Thanks to #Benno Kress
You can rewrite this in a safer way to check the size of the array, and use a ternary conditional:
if let str2 = (arr.count > 2 ? arr[2] : nil) as String?
Asserting if an array index exist:
This methodology is great if you don't want to add extension sugar:
let arr = [1,2,3]
if let fourthItem = (3 < arr.count ? arr[3] : nil ) {
Swift.print("fourthItem: \(fourthItem)")
}else if let thirdItem = (2 < arr.count ? arr[2] : nil) {
Swift.print("thirdItem: \(thirdItem)")
}
//Output: thirdItem: 3
extension Array {
func isValidIndex(_ index : Int) -> Bool {
return index < self.count
}
}
let array = ["a","b","c","d"]
func testArrayIndex(_ index : Int) {
guard array.isValidIndex(index) else {
print("Handle array index Out of bounds here")
return
}
}
It's work for me to handle indexOutOfBounds.
Swift 4 and 5 extension:
As for me I think this is the safest solution:
public extension MutableCollection {
subscript(safe index: Index) -> Element? {
get {
return indices.contains(index) ? self[index] : nil
}
set(newValue) {
if let newValue = newValue, indices.contains(index) {
self[index] = newValue
}
}
}
}
Example:
let array = ["foo", "bar"]
if let str = array[safe: 1] {
print(str) // "bar"
} else {
print("index out of range")
}
I believe the existing answers could be improved further because this function could be needed in multiple places within a codebase (code smell when repeating common operations). So thought of adding my own implementation, with reasoning for why I considered this approach (efficiency is an important part of good API design, and should be preferred where possible as long as readability is not too badly affected). In addition to enforcing good Object-Oriented design with a method on the type itself, I think Protocol Extensions are great and we can make the existing answers even more Swifty. Limiting the extension is great because you don’t create code you don’t use. Making the code cleaner and extensible can often make maintenance easier, but there are trade-offs (succinctness being the one I thought of first).
So, you can note that if you'd ONLY like to use the extension idea for reusability but prefer the contains method referenced above you can rework this answer. I have tried to make this answer flexible for different uses.
TL;DR
You can use a more efficient algorithm (Space and Time) and make it extensible using a protocol extension with generic constraints:
extension Collection where Element: Numeric { // Constrain only to numerical collections i.e Int, CGFloat, Double and NSNumber
func isIndexValid(index: Index) -> Bool {
return self.endIndex > index && self.startIndex <= index
}
}
// Usage
let checkOne = digits.isIndexValid(index: index)
let checkTwo = [1,2,3].isIndexValid(index: 2)
Deep Dive
Efficiency
#Manuel's answer is indeed very elegant but it uses an additional layer of indirection (see here). The indices property acts like a CountableRange<Int> under the hood created from the startIndex and endIndex without reason for this problem (marginally higher Space Complexity, especially if the String is long). That being said, the Time Complexity should be around the same as a direct comparison between the endIndex and startIndex properties because N = 2 even though contains(_:) is O(N) for Collections (Ranges only have two properties for the start and end indices).
For the best Space and Time Complexity, more extensibility and only marginally longer code, I would recommend using the following:
extension Collection {
func isIndexValid(index: Index) -> Bool {
return self.endIndex > index && self.startIndex <= index
}
}
Note here how I've used startIndex instead of 0 - this is to support ArraySlices and other SubSequence types. This was another motivation to post a solution.
Example usage:
let check = digits.isIndexValid(index: index)
For Collections in general, it's pretty hard to create an invalid Index by design in Swift because Apple has restricted the initializers for associatedtype Index on Collection - ones can only be created from an existing valid Collection.Index (like startIndex).
That being said, it's common to use raw Int indices for Arrays because there are many instances when you need to check random Array indices. So you may want to limit the method to fewer structs...
Limit Method Scope
You will notice that this solution works across all Collection types (extensibility), but you can restrict this to Arrays only if you want to limit the scope for your particular app (for example, if you don't want the added String method because you don't need it).
extension Array {
func isIndexValid(index: Index) -> Bool {
return self.endIndex > index && self.startIndex <= index
}
}
For Arrays, you don't need to use an Index type explicitly:
let check = [1,2,3].isIndexValid(index: 2)
Feel free to adapt the code here for your own use cases, there are many types of other Collections e.g. LazyCollections. You can also use generic constraints, for example:
extension Collection where Element: Numeric {
func isIndexValid(index: Index) -> Bool {
return self.endIndex > index && self.startIndex <= index
}
}
This limits the scope to Numeric Collections, but you can use String explicitly as well conversely. Again it's better to limit the function to what you specifically use to avoid code creep.
Referencing the method across different modules
The compiler already applies multiple optimizations to prevent generics from being a problem in general, but these don't apply when the code is being called from a separate module. For cases like that, using #inlinable can give you interesting performance boosts at the cost of an increased framework binary size. In general, if you're really into improving performance and want to encapsulate the function in a separate Xcode target for good SOC, you can try:
extension Collection where Element: Numeric {
// Add this signature to the public header of the extensions module as well.
#inlinable public func isIndexValid(index: Index) -> Bool {
return self.endIndex > index && self.startIndex <= index
}
}
I can recommend trying out a modular codebase structure, I think it helps to ensure Single Responsibility (and SOLID) in projects for common operations. We can try following the steps here and that is where we can use this optimisation (sparingly though). It's OK to use the attribute for this function because the compiler operation only adds one extra line of code per call site but it can improve performance further since a method is not added to the call stack (so doesn’t need to be tracked). This is useful if you need bleeding-edge speed, and you don’t mind small binary size increases. (-: Or maybe try out the new XCFrameworks (but be careful with the ObjC runtime compatibility for < iOS 13).
I think we should add this extension to every project in Swift
extension Collection {
#inlinable func isValid(position: Self.Index) -> Bool {
return (startIndex..<endIndex) ~= position
}
#inlinable func isValid(bounds: Range<Self.Index>) -> Bool {
return (startIndex..<endIndex) ~= bounds.upperBound
}
#inlinable subscript(safe position: Self.Index) -> Self.Element? {
guard isValid(position: position) else { return nil }
return self[position]
}
#inlinable subscript(safe bounds: Range<Self.Index>) -> Self.SubSequence? {
guard isValid(bounds: bounds) else { return nil }
return self[bounds]
}
}
extension MutableCollection {
#inlinable subscript(safe position: Self.Index) -> Self.Element? {
get {
guard isValid(position: position) else { return nil }
return self[position]
}
set {
guard isValid(position: position), let newValue = newValue else { return }
self[position] = newValue
}
}
#inlinable subscript(safe bounds: Range<Self.Index>) -> Self.SubSequence? {
get {
guard isValid(bounds: bounds) else { return nil }
return self[bounds]
}
set {
guard isValid(bounds: bounds), let newValue = newValue else { return }
self[bounds] = newValue
}
}
}
note that my isValid(position:) and isValid(bounds:) functions is of a complexity O(1), unlike most of the answers below, which uses the contains(_:) method which is of a complexity O(n)
Example usage:
let arr = ["a","b"]
print(arr[safe: 2] ?? "nil") // output: nil
print(arr[safe: 1..<2] ?? "nil") // output: nil
var arr2 = ["a", "b"]
arr2[safe: 2] = "c"
print(arr2[safe: 2] ?? "nil") // output: nil
arr2[safe: 1..<2] = ["c","d"]
print(arr[safe: 1..<2] ?? "nil") // output: nil