I am trying to calculate the average daily rating for workouts in my app. my results keeps coming back as NaN. My data base shows that there are ratings in there. Here is my average rating function
extension Appointment {
func averageReview() -> Double {
guard let workouts = workouts?.allObjects as? [Workout] else {
return 0
}
let total = Double(workouts.reduce(0) { $0 + $1.review})
let results = Double(total) / Double(workouts.count)
return results
}
}
You are dividing by workouts.count without making sure it's not equal to zero.
extension Appointment {
func averageReview() -> Double {
guard let workouts = workouts?.allObjects as? [Workout], workouts.count > 0 else {
return 0
}
let total = Double(workouts.reduce(0) { $0 + $1.review})
let results = Double(total) / Double(workouts.count)
return results
}
}
Related
I have a dictionary, let's say
let gamePrices = [
"Shooter": [40,60,80],
"RTS": [15,35,55]
"RPG": [5,40,70]
]
avgGamePrices(dictionary: [String: Int]) -> Double {
var total = 0;
for int in dictionary {
total += int
}
(i suppose, using of int is wrong here)
And i don't understand how to get each genre avg price, and total avg price (of all 9 values). I don't even know how to get total here, how should i "mention" the ints or strings of a dictionary in for loop?
For default array i'd use
for number in numbers {
total += number
}
let numbersTotal = Int(numbers.count);
let average = total/numbersTotal;
return (Double(average);
To get the average of all prices you need to first get all prices from your dictionary then you can calculate the average. It is not the same as calculating the average of the averages:
let gamePrices = [
"Shooter": [40,60,80],
"RTS": [15,35,55],
"RPG": [5,40,70]]
let allPrices = gamePrices.flatMap(\.value)
let sum = allPrices.reduce(.zero, +)
let average = Double(sum) / Double(allPrices.count) // 44.44444444444444
To get the maximum price for each genre:
let maxPrices: [(genre: String, maxPrice: Int)] = gamePrices.map { ($0, $1.max() ?? .zero)} // [(genre "Shooter", maxPrice 80), (genre "RTS", maxPrice 55), (genre "RPG", maxPrice 70)]
for (genre, max) in maxPrices {
print("Genre:", genre, "Max price:", max)
}
If you search a total AVG, so something like this:
func avgGamePrices(dictionary: [String: [Int]]) -> Double {
var total = 0;
var countElem = 0
for obj in dictionary {
countElem += obj.value.count
for v in obj.value {
total += v
}
}
return Double(total / countElem)
}
calling:
avgGamePrices(dictionary: gamePrices)
otherwise for each key:
func avg(for key: String, dictionary: [String: [Int]]) -> Double {
var total = 0;
let countElem = (dictionary[key] ?? []).count
for v in (dictionary[key] ?? []) {
total += v
}
return Double(total / countElem)
}
and you can call in this way (passing a single key of your dictionary like a param):
avg(for: "RTS", dictionary: gamePrices)
My code below fetches a string from core data and finds the sum of the array.I want to know find the average of all of the numbers in the array by dividing the sum by how many items are in the array. I just need someone to build off of this by dividing the amount of numbers by the sum that I have already found.
func performAction() {
let appD = UIApplication.shared.delegate as! AppDelegate
let context = appD.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Data")
request.returnsObjectsAsFaults = false
do {
let result = try context.fetch(request)
var retrievedData = [Double]()
for data in result as! [NSManagedObject] {
if let value = data.value(forKey: "ee") as? String {
retrievedData.append(Double(value) ?? 0)
}
}
let arraySum = retrievedData.reduce(0, +)
print(arraySum)
} catch {
print("Failed")
}
}
I'm running a for loop to get values returned from JSON. I'm getting the latitude values more specifically. I'm rounding the values using dblLat = (dblLat * 100).rounded() / 100
When appending the values to a new Array, only the first element is appended correctly as rounded, thereafter each new element is appended as a double with 15 decimal places:
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
//print(json)
var latArray = [Double]()
guard let array = json as? [Any] else { return }
for user in array {
guard let userDict = user as? [String: Any] else { return }
guard let userID = userDict["id"] as? Int else { print("No Int Value Present"); return }
guard let name = userDict["name"] as? String else { return }
print(name)
guard let company = userDict["company"] as? [String : String] else { return }
print(company)
guard let companyName = company["name"] as? String else { return }
print(companyName)
guard let address = userDict["address"] as? [String : Any] else { return }
guard let geo = address["geo"] as? [String : String] else { return }
print(geo)
guard let lat = geo["lat"] as? String else { return }
guard var dblLat = Double(lat) else { return }
dblLat = (dblLat * 100).rounded() / 100
print(dblLat)
latArray.append(dblLat)
print(latArray)
//print(lat)
print(userID)
}
} catch {
print(error)
}
Printing the array:
[-37.32, -43.950000000000003, -68.609999999999999, 29.460000000000001, -31.809999999999999, -71.420000000000002, 24.890000000000001, -14.4, 24.649999999999999, -38.240000000000002]
The Binary representation of some numbers is just infinite. Have a look here.
You could use a NumberFormatter to get strings with two decimal places
let array = [-37.32, -68.609999999999999, 29.460000000000001, -31.809999999999999, -71.420000000000002, 24.890000000000001, -14.4, 24.649999999999999, -38.240000000000002]
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
let roundedStringsArray = array.map {formatter.string(from: NSNumber(floatLiteral: $0))!}
print(roundedStringsArray)
I am trying to display the pervious entered exercise attributes in the current workout. If I go to the pervious workout everything shows up but when I go to my current workout the previous exercise attributes don't show and the date label only shows today's date and not the previous workout date. Here are the two functions for the issue. Let me know if i need to post more.
func lastWorkout() -> Workout? {
if let client = currentClient(), let currentWorkout = currentWorkout(), let workouts = client.workouts as? Set<Workout> {
// get all this client's workouts in cronological order
let sortedWorkouts = workouts.sorted { (one, two) -> Bool in
let scheduledTimeOfOne = one.appointment?.scheduled ?? Date(timeIntervalSince1970: 0)
let scheduledTimeOfTwo = two.appointment?.scheduled ?? Date(timeIntervalSince1970: 0)
return scheduledTimeOfOne > scheduledTimeOfTwo
}
// get the index of this workout
let indexOfTodaysWorkout = sortedWorkouts.index(of: currentWorkout) ?? 0
// go back one workout to find the last workout completed
let lastWorkout: Workout? = (indexOfTodaysWorkout - 1) < 0 ? nil : sortedWorkouts[indexOfTodaysWorkout - 1]
// and return
return lastWorkout
}
return nil
}
/// Last Exercise Info to load previous exercise data
func lastExercise() -> Exercise? {
guard let selectedExercise = currentExerciseInfo() else{
return nil
}
if let exercises = lastWorkout()?.exercises as? Set<Exercise>, let pastExercise = exercises.first(where: { $0.exerciseInfo == selectedExercise }) {
return pastExercise
}
return nil
}
So the array count was off in last workout function. Here is what the working function looks like. I am still not displaying the proper date. it just gives today's date.
func lastWorkout() -> Workout? {
if let client = currentClient(), let currentWorkout = currentWorkout(), let workouts = client.workouts as? Set<Workout> {
// get all this client's workouts in cronological order
let sortedWorkouts = workouts.sorted { (one, two) -> Bool in
let scheduledTimeOfOne = one.appointment?.scheduled ?? Date(timeIntervalSince1970: 0)
let scheduledTimeOfTwo = two.appointment?.scheduled ?? Date(timeIntervalSince1970: 0)
return scheduledTimeOfOne > scheduledTimeOfTwo
}
// get the index of this workout
let indexOfTodaysWorkout = sortedWorkouts.index(of: currentWorkout) ?? 0
// go back one workout to find the last workout completed
let lastWorkout: Workout? = sortedWorkouts.count < 2 ? nil : sortedWorkouts[indexOfTodaysWorkout + 1]
// and return
return lastWorkout
}
return nil
}
In my code, checkTheNextTime() function's array contains the strings 00.00 to 23.59. By writing this function I want to find the nearest future time. But when I tried with timeTable(shown in code) it returns 23.30 instead of 23.32(Now is 22.24). I guess the compiler search the array right to left. How can I find the nearest future time?
var timeTable = ["09.00","10.20","10.35","11.55","12.00","12.40","13.20","14.40","14.50", "23.00", "23.30", "23.31", "23.32"]
func checkTheNextTime(array array: Array<String>) -> String{
var nextTime: String?
for index in array {
let generatedString:String = getTimeAsMinToCheck(finalTime: index)
let indexInt = Int(generatedString)
if indexInt > 0{
nextTime = index
}
}
return nextTime!
}
func getTimeAsMinToCheck(finalTime finalTime: String) -> String{
let date = NSDate()
let formatter = NSDateFormatter()
formatter.timeStyle = NSDateFormatterStyle.NoStyle
formatter.dateStyle = NSDateFormatterStyle.ShortStyle
let now = formatter.stringFromDate(date)
formatter.locale = NSLocale.systemLocale()
formatter.dateFormat = "M/dd/yy HH.mm"
let datetofinish = formatter.dateFromString("\(now) \(finalTime)")
let finishDate: NSDate = datetofinish!
let secondsFromNowToFinish = finishDate.timeIntervalSinceNow
let minutes = Int(secondsFromNowToFinish / 60)
return String(minutes)
}
Assuming 23 is the right answer (its not clear from the comments above), here a a solution using swift 2.0 and closures
map your timeTable array into an array of delta's from the current
time (invalid entries are mapped to 0)
add the minimum delta to the time now
let timeNow: Float = 22.24
let timeTable = ["09.00","10.20","10.35","11.55","12.00","12.40","13.20","14.40","14.50", "23.00", "23.30", "23.31", "23.32"]
let minDelta = timeTable
.map { Float(NSNumberFormatter().numberFromString($0) ?? 0.0) - timeNow }
.filter { $0 > 0 }
.minElement()
let nextTime = (minDelta ?? 0) + timeNow
print(nextTime) // 23.0
This code should work for your requirement:
Done in Swift 2.0:
var timeTable = ["09.00","10.20","10.35","11.55","12.00","12.40","13.20","14.40","14.50", "23.00", "23.30", "23.31", "23.32"]
func checkTheNextTime(array array: Array<String>) -> String{
let currentTime:String = getTimeAsMinToCheck(finalTime: "23.24") // set this value by calculating from current time
let currentTimeInt = Int(currentTime)// Int value of currentTime
var nextTime: String? //this will hold the nearest future value
var minDiff: Int = 24*60 //lets start with maximum value
for index in timeTable {
let generatedString:String = getTimeAsMinToCheck(finalTime: index)
let indexInt = Int(generatedString)
if (indexInt > currentTimeInt) { //checking for future time only
let timeDiff = indexInt - currentTimeInt // this will be positive
if (timeDiff < minDiff) {
minDiff = timeDiff //update minDiff as timeDiff is less than minDiff
nextTime = index
}
}
}
return nextTime!
}