Swift 4.2 closures - swift

Hi I'm learning swift I have an exercise on closures used to filter collections
I have two simple closures that are used to filter and map a dictionary
let myDict: [String: Int] = ["Dan":38, "Kira":2, "Olga":33, "Jess":10, "Bobo":4]
let filteredMyDict = myDict.filter {
return $0.value < 5
}
print(filteredMyDict)
let filteredNames = filteredMyDict.map {
return $0.key
}
print(filteredNames)
Is it possible to chain the filter and map statement , if so how.
That

You can chain filter and map
let filteredNames = myDict.filter { $0.value < 5 }
.map { $0.key }
or use compactMap to get the result with a single traversal of the
dictionary:
Returns an array containing the non-nil results of calling the given transformation with each element of this sequence.
In your case:
let filteredNames = myDict.compactMap {
$0.value < 5 ? $0.key : nil
}

Related

In swift 5, how to retrieve the key of the second low value in a dictionary of [String: Float]?

I have a dictionary:
var mydict: [String: Float] = ["aze": 22.4, "lkh": 42.04, etc ... ]
How to retrieve the key of the second low value in mydict?
You can use sorted(by:) to sort the Dictionary into ascending order based on the values, then simply get the 2nd element from the sorted array of key-value pairs and access its key property.
var mydict: [String: Float] = ["aze": 22.4, "lkh": 42.04, "abc": 25.12 ]
let ascendingDict = mydict.sorted(by: { $0.value < $1.value })
let secondLowest = ascendingDict[1]
secondLowest.key // "abc"
Here is another solution that also handles dictionaries that contain 1 element or less by returning nil in that case.
The solution is using a tuple of optional tuples (key/value) and the second lowest key is then found by using reduce(into:) on the dictionary
let secondLowest = mydict.reduce(((String, Float)?, (String,Float)?)(nil, nil)) {
guard let first = $0.0 else {
return ($1, nil)
}
guard let second = $0.1 else {
if first.1 < $1.value { return (first, $1) }
return ($1, first)
}
return $1.value < first.1 ? ($1, first) : ($1.value < second.1 ? (first, $1) : $0)
}.1?.0
this could also be expressed in an extension to Dictionary as a computed property
extension Dictionary where Value: Comparable {
var keyForSecondLowest: Key? {
self.reduce(((Key, Value)?, (Key,Value)?)(nil, nil)) {
guard let first = $0.0 else {
return ($1, nil)
}
guard let second = $0.1 else {
if first.1 < $1.value { return (first, $1) }
return ($1, first)
}
return $1.value < first.1 ? ($1, first) : ($1.value < second.1 ? (first, $1) : $0)
}.1?.0
}
}
Example
if let secondLowest = mydict.keyForSecondLowest {
print(secondLowest)
}
Another approach is via pattern matching within reduce():
let minims = mydict.reduce(into: ((String, Float)?.none, (String, Float)?.none)) {
switch ($0.0?.1 ?? .infinity, $0.1?.1 ?? .infinity, $1.1) {
case let (min1, _, val) where val < min1: $0 = ($1, $0.0)
case let (_, min2, val) where val < min2: $0.1 = $1
default: break
}
}
print(minims.1?.0)
The algorithm is the same as the Joakim's one, and it works by computing a tuple of optional tuples, the first optional tuple corresponding to the minimal entry in the dictionary, while the second will correspond to the next minimal entry.
Note that if the dictionary has less than two elements then some of the result tuples will be nil.
Performance is linear, as it requires only one iteration of the dictionary.

Random Elements from Dictionary

Here is my Array of Dictionary,
var myArrayOfDict = [["vegetables": ["CARROT","BEANS"], "fruits": ["APPLE","MANGO","BANANA"], "letters":["A","B","C","D"],"numbers":["ONE","TWO","THREE"],"shapes":["SQUARE","RECTANGLE","CIRCLE"]]]
How do i get the desired output, actually i need to get random selected elements of the specified range ...(i.e) when i need 3 elements randomnly from dictionary as like,
[["fruits": ["APPLE","MANGO","BANANA"],"shapes":["SQUARE","RECTANGLE","CIRCLE"],"numbers":["ONE","TWO","THREE"]]]
When i need just 2 elements randomnly like,
[["shapes":["SQUARE","RECTANGLE","CIRCLE"],"fruits": ["APPLE","MANGO","BANANA"]]]
Thanks in Advance,
Here is one solution using randomElement().
func randomSelection(from dict: [String: [String]], count: Int) -> [String: [String]] {
guard !dict.isEmpty else { return [:] }
var result = [String: [String]]()
for i in 0..<count {
let element = dict.randomElement()! //We know dictionary is not empty
result[element.key] = element.value
}
return result
}
The above solution might return less elements in a dictionary than expected if the same element is returned more than once from randomElemnt(). If this should be voided the below solution should work
func randomSelection(from dict: [String: [String]], count: Int) -> [String: [String]] {
guard !dict.isEmpty else { return [:] }
guard dict.count > count else { return dict }
var result = [String: [String]]()
while result.count < count {
let element = dict.randomElement()!
if result[element.key] == nil {
result[element.key] = element.value
}
}
return result
}
Since the function takes a dictionary as the first argument the array needs to be looped over
for d in myArrayOfDict {
print(randomSelection(from: d, count: 2))
}
Array myArrayOfDict contains a single Dictionary. So, it doesn't make sense getting a random element from it.
As your example explains, you need to get random elements from the Dictionary itself.
So, you can use randomElement to get that working.
let myArrayOfDict = ["vegetables": ["CARROT","BEANS"], "fruits": ["APPLE","MANGO","BANANA"], "letters":["A","B","C","D"],"numbers":["ONE","TWO","THREE"],"shapes":["SQUARE","RECTANGLE","CIRCLE"]]
var elements = [String : [String]]()
let count = 2
for _ in 0..<count {
if let element = myArrayOfDict.randomElement() {
elements[element.key] = element.value
}
}
print(elements)

Swiftier Swift for 'add to array, or create if not there...'

I've noticed a common pattern in Swift is
var x:[String:[Thing]] = [:]
so, when you want to "add an item to one of the arrays", you can not just
x[which].append(t)
you have to
if x.index(forKey: which) == nil {
x[which] = []
}
x[which]!.append(s!)
Really, is there a swiftier way to say something like
x[index?!?!].append??(s?!)
While this is a question about style, performance seems to be a critical issue when touching arrays in Swift, due to the copy-wise nature of Swift.
(Please note, obviously you can use an extension for this; it's a question about Swiftiness.)
Swift 4 update:
As of Swift 4, dictionaries have a subscript(_:default:) method, so that
dict[key, default: []].append(newElement)
appends to the already present array, or to an empty array. Example:
var dict: [String: [Int]] = [:]
print(dict["foo"]) // nil
dict["foo", default: []].append(1)
print(dict["foo"]) // Optional([1])
dict["foo", default: []].append(2)
print(dict["foo"]) // Optional([1, 2])
As of Swift 4.1 (currently in beta) this is also fast,
compare Hamish's comment here.
Previous answer for Swift <= 3: There is – as far as I know – no way to "create or update" a dictionary
value with a single subscript call.
In addition to what you wrote, you can use the nil-coalescing operator
dict[key] = (dict[key] ?? []) + [elem]
or optional chaining (which returns nil if the append operation
could not be performed):
if dict[key]?.append(elem) == nil {
dict[key] = [elem]
}
As mentioned in SE-0154 Provide Custom Collections for Dictionary Keys and Values and also by #Hamish in the comments, both methods
make a copy of the array.
With the implementation of SE-0154 you will be able to mutate
a dictionary value without making a copy:
if let i = dict.index(forKey: key) {
dict.values[i].append(elem)
} else {
dict[key] = [key]
}
At present, the most efficient solution is given by Rob Napier
in Dictionary in Swift with Mutable Array as value is performing very slow? How to optimize or construct properly?:
var array = dict.removeValue(forKey: key) ?? []
array.append(elem)
dict[key] = array
A simple benchmark confirms that "Rob's method" is the fastest:
let numKeys = 1000
let numElements = 1000
do {
var dict: [Int: [Int]] = [:]
let start = Date()
for key in 1...numKeys {
for elem in 1...numElements {
if dict.index(forKey: key) == nil {
dict[key] = []
}
dict[key]!.append(elem)
}
}
let end = Date()
print("Your method:", end.timeIntervalSince(start))
}
do {
var dict: [Int: [Int]] = [:]
let start = Date()
for key in 1...numKeys {
for elem in 1...numElements {
dict[key] = (dict[key] ?? []) + [elem]
}
}
let end = Date()
print("Nil coalescing:", end.timeIntervalSince(start))
}
do {
var dict: [Int: [Int]] = [:]
let start = Date()
for key in 1...numKeys {
for elem in 1...numElements {
if dict[key]?.append(elem) == nil {
dict[key] = [elem]
}
}
}
let end = Date()
print("Optional chaining", end.timeIntervalSince(start))
}
do {
var dict: [Int: [Int]] = [:]
let start = Date()
for key in 1...numKeys {
for elem in 1...numElements {
var array = dict.removeValue(forKey: key) ?? []
array.append(elem)
dict[key] = array
}
}
let end = Date()
print("Remove and add:", end.timeIntervalSince(start))
}
Results (on a 1.2 GHz Intel Core m5 MacBook) for 1000 keys/1000 elements:
Your method: 0.470084965229034
Nil coalescing: 0.460215032100677
Optional chaining 0.397282958030701
Remove and add: 0.160293996334076
And for 1000 keys/10,000 elements:
Your method: 14.6810429692268
Nil coalescing: 15.1537700295448
Optional chaining 14.4717089533806
Remove and add: 1.54668599367142

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
}

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.