Increment number in Dictionary - swift

I have a Dictionary [String:AnyObject] which contains some keys and values.
I want to increment a key to which value is of type Double.
I can do it this way:
let nr = dict["number"] as! Double
dict["number"] = nr + 10
But I dont like that way so Im wondering if there is another way
I tried this:
(dict["number"] as! Double) += 10
But that gives me an error:
Binary operator '+=' cannot be applied to operands of type '(Double)' and 'Double'
Why isn't this working?

Following is an alternative. If you want to avoid force unwrapping an optional:
dict["number"] = (dict["number"] ?? 0) + 10

You are close and in fact you can write to a dictionary using +=, the problem is your cast. For example we can do:
var dict = ["number" : 2]
dict["number"]! += 10
and now dict["number"] returns 12. Now this is creating a new value (12) and replacing it into the dictionary, it is just a clean way of looking at it.
The problem with your code is that the left side (dict["number"] as! Double) gives you a Double. So you have say (12), then the right side is a Double too (10). So your code ends up looking like (12) += 10 which you can clearly see as problematic, since this is equivalent to 12 = 12 + 10, yikes!
So that being said, you can use the my first solution if you are working with native Swift dictionaries, otherwise your solved solution above works too, just a bit longer.
Lastly, if you are really looking for a one liner that works with your exact situation you should be able to do something like:
dict["number"] = (dict["number"] as! Double) + 10

Another option:
dict["number", default: 0] += 10

The safe way of casting would be:
if let num = dict["number"] as? Double {
dict["number"] = num + 10
}

Related

Swift 4: How to compare doubles to another?

i have 3 Doubles, their values are random, and i am trying to achieve a comparison between them
so my code is
let double1 = 10.00 // notes: they're all random between 1-100 so i don't know what this will be
let double2 = 7.66
let double3 = 6.43
but, i tried
if (double1?.isLess(than: 50.0))! {
print("Low")
} else {
print("High")
}
but as i mentioned, this can't be done due to the random values, i need to compare two of them to each other, to make sure that one of them will be higher
Its actually rather very simple:
let maxDouble = max(max(double1, double2), double3) // prints 10

Combining map with split

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]

Can this be more Swift3-like?

What I want to do is populate an Array (sequence) by appending in the elements of another Array (availableExercises), one by one. I want to do it one by one because the sequence has to hold a given number of items. The available exercises list is in nature finite, and I want to use its elements as many times as I want, as opposed to a multiple number of the available list total.
The current code included does exactly that and works. It is possible to just paste that in a Playground to see it at work.
My question is: Is there a better Swift3 way to achieve the same result? Although the code works, I'd like to not need the variable i. Swift3 allows for structured code like closures and I'm failing to see how I could use them better. It seems to me there would be a better structure for this which is just out of reach at the moment.
Here's the code:
import UIKit
let repTime = 20 //seconds
let restTime = 10 //seconds
let woDuration = 3 //minutes
let totalWOTime = woDuration * 60
let sessionTime = repTime + restTime
let totalSessions = totalWOTime / sessionTime
let availableExercises = ["push up","deep squat","burpee","HHSA plank"]
var sequence = [String]()
var i = 0
while sequence.count < totalSessions {
if i < availableExercises.count {
sequence.append(availableExercises[i])
i += 1
}
else { i = 0 }
}
sequence
You can overcome from i using modulo of sequence.count % availableExercises.count like this way.
var sequence = [String]()
while(sequence.count < totalSessions) {
let currentIndex = sequence.count % availableExercises.count
sequence.append(availableExercises[currentIndex])
}
print(sequence)
//["push up", "deep squat", "burpee", "HHSA plank", "push up", "deep squat"]
You can condense your logic by using map(_:) and the remainder operator %:
let sequence = (0..<totalSessions).map {
availableExercises[$0 % availableExercises.count]
}
map(_:) will iterate from 0 up to (but not including) totalSessions, and for each index, the corresponding element in availableExercises will be used in the result, with the remainder operator allowing you to 'wrap around' once you reach the end of availableExercises.
This also has the advantage of preallocating the resultant array (which map(_:) will do for you), preventing it from being needlessly re-allocated upon appending.
Personally, Nirav's solution is probably the best, but I can't help offering this solution, particularly because it demonstrates (pseudo-)infinite lazy sequences in Swift:
Array(
repeatElement(availableExercises, count: .max)
.joined()
.prefix(totalSessions))
If you just want to iterate over this, you of course don't need the Array(), you can leave the whole thing lazy. Wrapping it up in Array() just forces it to evaluate immediately ("strictly") and avoids the crazy BidirectionalSlice<FlattenBidirectionalCollection<Repeated<Array<String>>>> type.

adding doubles contained within a dictionary swift 3

I am learning a little bit about Swift and am following along with a Udemy course. The course is taught in swift 2 and I am using swift 3 so I am hoping to understand the difference in outputs and I cannot find any answer online thus far.
I have a dictionary item which contains 3 things.
var menu = ["entre" : 5.55, "main-meal": 20.50, "desert": 5.50]
The idea is to add the 3 values together using the instructors output (which works fine in swift 2):
var totalCost = menu["entre"]! + menu["desert"]! + menu["main-meal"]!
Within the course this works just fine but for me it throws an error that reads "Cannot subscript a value of type 'inout [String : Double]' (aka 'inout Dictionary')"
What I find very odd is that if I only use 2 values, all is fine, the problem is when the third is added. I can get around the issue by adding + 0.0 to the end as below:
var totalCost = menu["entre"]! + menu["desert"]! + menu["main-meal"]! + 0.0
What I am hoping to understand is what is the difference between the two versions and ideally what I am doing wrong in adding the 3 together without my workaround.
Thanks in advance.
Workarounds
For a few keys
let (entreCost, desertCost, mainCost) = (menu["entre"]!, menu["desert"]!, menu["main-meal"]!)
let totalCost = entreCost + desertCost + mainCost
For a lot of keys
let keysToSum = ["entre", "desert", "main-meal"]
keysToSum.map{ menu[$0]!}.reduce(0, +)
For all keys
menu.values.reduce(0, +)

How to convert Swift 3 output of readLine() to Integer?

Please don't mark as duplicate until you read the whole thing. This is specific to Swift 3.
I have functions that have parameters such as Ints, Floats, etc. I'd like to take the output of readLine() and have Swift accept the output of readLine() as these types, but unfortunately readLine() outputs a String? and when I try to convert it tells me it's not unwrapped. I need help. I'm using Ubuntu 16.04.
For example, if I had area(width: 15, height: 15), how would I replace 15 and 15 with two constants containing readLine() or any equivalent to readLine() to accept input from a user in the terminal?
Also note that the program I am writing is specifically doing math, as most people seem to be happy with strings, this is literally a CLI-based calculator.
EDIT 1 (lol) Okay, here's a more exact explanation of above. The following code will print the area of a trapezoid:
import Foundation
func areaTrapezoid(height: Float, baseOne: Float, baseTwo: Float) {
let inside = baseOne + baseTwo
let outside = 0.5 * height
let result = outside * inside
print("Area of Trapezoid is \(result)")
}
areaTrapezoid(height: 10, baseOne: 2, baseTwo: 3)
So, the trapezoid has a height of 10 units, and two bases that have lengths of 2 and 3 respectively. However, I want to do something like:
import Foundation
func areaTrapezoid(height: Float, baseOne: Float, baseTwo: Float) {
let inside = baseOne + baseTwo
let outside = 0.5 * height
let result = outside * inside
print("Area of Trapezoid is \(result)")
}
let h = readLine()
areaTrapezoid(height: h, baseOne: 2, baseTwo: 3)
Except, as is already obvious, readLine() will output an optional string, and not a Float. I want the user to be able to input the numbers via CLI in sort of an interactive way, if you will. I'm just learning Swift, but I did something similar in C++ when I was learning that language. Thanks for any help you can provide.
readLine() returns an Optional String.
To unwrap the String, you can use if let, and to convert the String to an integer, use Int().
Example:
import Foundation
if let typed = readLine() {
if let num = Int(typed) {
print(num)
}
}
Let's say you prompted the user twice:
let prompt1 = readLine()
let prompt2 = readLine()
Then:
if let response1 = prompt1,
response2 = prompt2,
num1 = Int(response1),
num2 = Int(response2) {
print("The sum of \(num1) and \(num2) is \(num1 + num2)")
}