Combining map with split - swift

From having following string: 12#17#15 I want to get an array with the numbers: [12, 17, 15].
I've tried following approach, but firstly I still get an error (Cannot convert value of type '[Double?]' to expected argument type 'Double?'), and obviously, I prefer to do it all on one map instead of such chain. Why do these types differ..? I'd say they should be matching...
let substrings = records?.split(separator: "#", maxSplits: Int.max, omittingEmptySubsequences: true).map(String.init).map(Double.init)
let objects = substrings.map {value in Model(value: value ?? 0)}

Unless there's some technique I've never heard of, you're not using map correctly.
Here's an example of the code you want:
let string = "12#17#15"
let objects = string.split(separator: "#").map {Double($0) ?? 0}
in Swift, map does something to every entry of an array, and then results in some sort of output. What's going on here is that first just doing a simple split (I'm going to assume you don't actually need the upper limit of an Int for the max results, but you can re-add that if you wish), and then initing a Double with each substring (which you call with $0). If trying to create that Double fails, then I'm coalescing it to a 0 instead of a nil.
If you don't want the Doubles that fail and return nil to be zero, then use flatmap {$0} instead

I would use flatMap instead of map, as Double init with String can return optional.
let records = "12#17#15"
let substrings = records.split(separator: "#").flatMap {Double($0)}
print(substrings) // [12.0, 17.0, 15.0]

Related

Ordering of Dictionary Swift

I'm trying to work through a problem at the moment which is currently doing the rounds on the internet. The problem is: Given an array of characters, find the first non repeating character. I had a go at it and solved it but I was curious about how other people solved it so I did some looking around and found this answer:
let characters = ["P","Q","R","S","T","P","R","A","T","B","C","P","P","P","P","P","C","P","P","J"]
var counts: [String: Int] = [:]
for character in characters {
counts[character] = (counts[character] ?? 0) + 1
}
let nonRepeatingCharacters = characters.filter({counts[$0] == 1})
let firstNonRepeatingCharacter = nonRepeatingCharacters.first!
print(firstNonRepeatingCharacter) //"Q"
Source: Finding the first non-repeating character in a String using Swift
What I don't understand about this solution, is why it always returns Q, when there are other elements "S" "A" "B" and "J" that could be put first when the filter is applied to the dictionary. My understanding of dictionaries is that they are unordered, and when you make one they change from run to run. So if I make one:
let dictionary:[String:Int] = ["P": 9, "C": 8, "E": 1]
And then print 'dictionary', the ordering will be different. Given this, can anyone explain why the solution above works and maintains the order in which the dictionary elements were added?
You are not looking correctly at the code. The filter is not applied to a dictionary. It is applied to the array (characters), which has a defined order. The dictionary is used only to store counts.

Swift: Get max value for key in array of dictionaries

I have an array containing dictionaries.
let arr = [["test":1], ["test":2], ["test":3], ["test":4]]
I now need to get the one dictionary that contains the highest value for the key "test" (without iterating through everything). I was thinking about filter(_:) but this will only filter out. map(_:) also does not work as I need to filter and not to map.
There's an example how to get the key with the highest value in a dictionary but this does not work in this case.
let hues = ["Heliotrope": 296, "Coral": 16, "Aquamarine": 156]
let greatestHue = hues.max { a, b in a.value < b.value }
print(greatestHue)
Any help is appreciated!
You can use max much like in your example.
let arr = [["test":1], ["test":4], ["test":3], ["test":2]]
print(arr.max { $0["test"]! < $1["test"]! })
This gives the dictionary with the highest value.
Of course the use of ! is bad unless it is guaranteed that each dictionary really has a "text" key.
Also note that using max will still result in the entire array being iterated. You can't avoid that unless your dictionaries are always sorted by the value. Then in that case simply use last instead of max.

Binary operator '+' cannot be applied to operands of type 'Int' and '[Int]'

I'm trying to write code that will update my array and give total pay based on the daily pay. I'm getting an error about binary operators so how do I fix this line code so that doesn't happen.
for day in stride(from: 1, to: 31, by: 1)
{
dailyPay[day] = [Int(pay)]
pay*=2
if(day==1)
{
totalPay[day] = Int(pay)
}
else
{
totalPay[day] = totalPay[day-1]+dailyPay[day]//The problem is Here
print("\(heade) \(day) \(head) \(dailyPay[day]) \(total) \(totalPay[day])")
}
You don't show the declarations of your variables, but it appears that totalPay is an array of Ints, whereas dailyPay is a two-dimensional array of arrays of Int. So, totalPay[day-1] will be an Int, whereas dailyPay[day] will be an [Int], or an array of Ints. The error you're getting therefore means exactly what it says; you can't use + to add an Int and an array.
From your code, it appears that dailyPay is probably meant to be a plain old array of integers, like totalPay. So you could fix this by changing the declaration, whereever it is, from:
var dailyPay: [[Int]]
to:
var dailyPay: [Int]
Then, change the assignment to:
dailyPay[day] = Int(pay)
and things should work.
Sidenote: Your for loop is needlessly complex. There's no need for stride, when you can just:
for day in 1...31

Array map vs. forEach in Swift

In Swift arrays you can do:
var myArray = [1,2,3,4]
myArray.forEach() { print($0 * 2) }
myArray.map() { print($0 * 2) }
They both do the same thing. The only difference is .map also returns an array of voids as well, [(),(),(),()], which gets unused. Does that mean .map performs worse than .forEach when it's not assigning to anything?
In Swift as per Apple's definition,
map
is used for returning an array containing the results of mapping the
given closure over the sequence’s elements
whereas,
 
forEach
calls the given closure on each element in the sequence in the same
order as a for-in loop.
Both got two different purposes in Swift. Even though in your example map works fine, it doesn't mean that you should be using map in this case.
map eg:-
let values = [1.0, 2.0, 3.0, 4.0]
let squares = values.map {$0 * $0}
[1.0, 4.0, 9.0, 16.0] //squares has this array now, use it somewhere
forEach eg:-
let values = [1.0, 2.0, 3.0, 4.0]
values.forEach() { print($0 * 2) }
prints below numbers. There are no arrays returned this time.
2.0
4.0
6.0
8.0
In short to answer your questions, yes the array generated from map is wasted and hence forEach is what you should use in this case.
Update:
OP has commented that when he tested, the performance was better for map compared to forEach. Here is what I tried in a playground and found. For me forEach performed better than map as shown in image. forEach took 51.31 seconds where as map took 51.59 seconds which is 0.28 seconds difference. I don't claim that forEach is better based on this, but both has similar performance attributes and which one to use, depends on the particular use case.
According to Apple Doc
.map
The map(_:) method calls the closure expression once for each item in
the array. You do not need to specify the type of the closure’s input
parameter, number, because the type can be inferred from the values in
the array to be mapped.
.forEach(_:)
Apple Doc
Calls the given closure on each element in the sequence in the same
order as a for-in loop.
var myArray = [1,2,3,4]
var sampleArray = [1,2,3,4]
//myArray = myArray.forEach() { ($0 * 2) }//Not allowed
sampleArray = sampleArray.map() { ($0 * 2) }
print("sampleArray array is \(sampleArray)")//sampleArray array is [2, 4, 6, 8]
.map is faster than .forEach.
Just by a hair. Note that assigning .map to _ as pictured causes the closure to be required not to return a value.
A wild compiler optimization appears!

How to compare Range<String.Index> and DefaultBidirectionalIndices<String.CharacterView>?

This comparison worked in Swift 2 but doesn't anymore in Swift 3:
let myStringContainsOnlyOneCharacter = mySting.rangeOfComposedCharacterSequence(at: myString.startIndex) == mySting.characters.indices
How do I compare Range and DefaultBidirectionalIndices?
From SE-0065 – A New Model for Collections and Indices
In Swift 2, collection.indices returned a Range<Index>, but because a range is a simple pair of indices and indices can no longer be advanced on their own, Range<Index> is no longer iterable.
In order to keep code like the above working, Collection has acquired an associated Indices type that is always iterable, ...
Since rangeOfComposedCharacterSequence returns a range of
character indices, the solution is not to use indices, but
startIndex..<endIndex:
myString.rangeOfComposedCharacterSequence(at: myString.startIndex)
== myString.startIndex..<myString.endIndex
As far as I know, String nor String.CharacterView does not have a concise method returning Range<String.Index> or something comparable to it.
You may need to create a Range explicitly with range operator:
let myStringContainsOnlyOneCharacter = myString.rangeOfComposedCharacterSequence(at: myString.startIndex)
== myString.startIndex..<myString.endIndex
Or compare only upper bound, in your case:
let onlyOne = myString.rangeOfComposedCharacterSequence(at: myString.startIndex).upperBound
== myString.endIndex