How to find an average for dictionary in Swift? - arrays

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)

Related

Increment value in an array of dictionaries

I'm trying to calculate the number of times a specific number is rolled in a set of six six-sided dice, to determine wether or not I have three of a kind, four of a kind, etc.
I can pull the face value of each die rolled and compare it to the faces on a 6 sided die but can't get the "qtyRolled" key/value to increment.
func rollDice() {
currentRoll.removeAll()
for _ in currentDiceArray {
let num: UInt32 = arc4random_uniform(UInt32(currentDieFaceArray.count))
let currentDieData = currentDieFaceArray[Int(num)]
let faceValue = currentDieData["faceValue"]
currentRoll.append(faceValue as! Int)
}
print(currentRoll)
getQtyOfDieFaces()
//checkForScoringCombos()
}
func getQtyOfDieFaces() {
for die in currentRoll {
for dieData in currentDieFaceArray {
var currentDieData = dieData
let qtyRolled = currentDieData["qtyRolled"] as! Int
let faceValue = currentDieData["faceValue"] as! Int
print("faceValue: \(faceValue)")
print("Die: \(die)")
if faceValue == die {
currentDieData["qtyRolled"] = qtyRolled + 1 as AnyObject
}
}
}
for currentDieData in currentDieFaceArray {
print(currentDieData["qtyRolled"]!)
}
}
Here are my data structures
var currentDieFaceArray = [[String:AnyObject]]()
var currentDiceArray:[[String:AnyObject]] = [[:]]
var currentRoll: [Int] = []
I'd recommend ditching the dictionaries unless you really need them, as you're really just dealing with properties of a struct/class. I'm going to assume you're using the currentDieFaceArray method so that you can make this generic for non-linear dice faces of other dimensions (e.g. you can have a four-sided dice with the face values [1, 4, 6, 8]). If this isn't the case, you can simplify further I'm sure with a simple array of counts. But here's an example with your method (probably has other possible optimisations).
class DieFaceDefn
{
let faceValue : Int
var countThisRoll : Int = 0
init(faceValue: Int)
{
self.faceValue = faceValue
}
}
var diceFaces: [DieFaceDefn] = []
let numberOfCurrentDice = 5
func setupDice()
{
diceFaces.append(DieFaceDefn(faceValue: 1))
diceFaces.append(DieFaceDefn(faceValue: 2))
...
}
var currentRoll: [Int] = []
func rollDice()
{
currentRoll.removeAll()
diceFaces.forEach { $0.countThisRoll = 0 }
for _ in 0..<numberOfCurrentDice
{
let num: UInt32 = arc4random_uniform(UInt32(diceFaces.count))
let currentDieData = diceFaces[Int(num)]
let faceValue = currentDieData.faceValue
currentRoll.append(faceValue)
currentDieData.countThisRoll += 1
}
print(currentRoll)
diceFaces.forEach { print("\($0.faceValue): \($0.countThisRoll)") }
}

Swift average of items in database

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
}
}

How to group the same element in array and sum it in Swift?

How to filter duplicate category element into different array and count their amount?
This is the format, record is from the core data.
var record = [Record]()
[<Record:...; data: {accountbook = "MyBook";
amount = "10.50";
category = "A";
id = 1;
},<Record:...; data: {accountbook = "MyBook";
amount = "5.50";
category = "B";
id = 2;
},<Record:...; data: {accountbook = "MyBook";
amount = "4.50";
category = "B";
id = 3;
}]
What I want
var category = ["A", "B"] //success
var total = [10.50, 10.00]
This is what I do for finding the category, and it works but how to group the same category and sum the total?
var category =[String]()
for categoryObject in record{
if let categoryItem = categoryObject.category{
category.append(categoryItem)
}
}
//I tried this code to group the same category but fail.
let result = Set(record).map{ category in return record.filter{$0 == category} }
Another way is this. but how if I have A-Z category? it will have very long code..Is there any way can detect the same value and split it to different array so that I can sum it by category.
categoryFilter = record.filter { $0.category!.contains("A") }
First group your record object by category like this
extension Sequence {
func group<GroupingType: Hashable>(by key: (Iterator.Element) -> GroupingType) -> [[Iterator.Element]] {
var groups: [GroupingType: [Iterator.Element]] = [:]
var groupsOrder: [GroupingType] = []
forEach { element in
let key = key(element)
if case nil = groups[key]?.append(element) {
groups[key] = [element]
groupsOrder.append(key)
}
}
return groupsOrder.map { groups[$0]! }
}
}
Then you will get your distinct arrays based on category like this
var records : [Record] = []// your objects
let distinctRecords = records.group(by: {$0. category})
Now you can use reduce to calculate sum of values of that category
for items in distinctRecords{
let sum = items.reduce(0.0){$0.0 + $1. amount ?? 0.0}// assuming you have float values in your amount
print(items)// do whatever you want to do with your distinct array
print(" \(sum)")
}
#Wan Jern I have written a piece of code, you can try this one. Hopefully, it will work.
var category = [String]()
var totalArr = [CGFloat]()
for categoryObject in record{
if let categoryItem = categoryObject.category{
if !category.contains(categoryItem) {
category.append(categoryItem)
totalArr.append(categoryObject.amount)
} else {
let index = category.index(of: categoryItem)
let itemAtIndex = category[index]
let itemAtIndex = itemAtIndex + categoryObject.amount
totalArr.insert(itemAtIndex, at: index)
}
}
}
Do you have your record struct in a class model?
like my data model selected from sqlite:
//Data model
import Foundation
import UIKit
class scoreModel: NSObject {
var lessonName:String = String()
var lessonCode:String = String()
var creditPoint:Double = Double()
var totalStudentNumber:Int = Int()
var teacherName:String = String()
var semesterName:String = String()
var scoreValue:String = String()
var studentCount:Int = Int()
}
If the answer is yes, we can use pointer in array and repeat while loop to do this manually.
Like my code:
let mysql = ""
let dataArray = SQLiteManager.shareInstance.queryDB(sql:mysql)
var i = 0
while i<dataArray.count-1
{
var scoreArray = [Dictionary<String, Int>]()
var range = 0
var test = 0
test = i
//print("pointer i is'\(test)'")
while ((dataArray[test]as! scoreModel).lessonCode == (dataArray[test+range]as! scoreModel).lessonCode && (test+range)<dataArray.count-1)
{
let key = (dataArray[test+range]as! scoreModel).scoreValue
let value = (dataArray[test+range]as! scoreModel).studentCount
var dict: [String: Int] = [String: Int]()
dict[key] = value
scoreArray.append(dict)
//print("working pointer is'\(test+range)'")
range = range+1
}
//transfer array
let model:resultModel = resultModel()
model.lessonName = (dataArray[test]as! scoreModel).lessonName
model.lessonCode = (dataArray[test]as! scoreModel).lessonCode
model.creditPoint = (dataArray[test]as! scoreModel).creditPoint
model.semesterName = (dataArray[test]as! scoreModel).semesterName
model.teacherName = (dataArray[test]as! scoreModel).teacherName
model.totalStudentNumber = (dataArray[test]as! scoreModel).totalStudentNumber
model.scoreArray = scoreArray
resultArray.add(model)
i = i+range
//print("range is'\(range)'")
}
Make the Records as Hashable with "category" as the unique in stubs function
struct Record: Hashable {
var accountbook = ""
var category = ""
var amount = 0.0
// added from stubs of Hashable
var hashValue: Int { return category.hashValue }
static func ==(lhs: Record, rhs: Record) -> Bool {
return lhs.category == rhs.category
}
}
Then filter the unique categories
let categories = Set(record).map { $0.category }
print(categories) // ["B", "A"]
And make a sum of each category
let totals = categories.map { c in
record.filter { $0.category == c }.map{ $0.amount }.reduce(0, +)
}
print(totals) // get the sums as [10.0, 10.5]

Finding indices of max value in swift array

I have 2 arrays. One for players and one for scores. e.g.
var players = ["Bill", "Bob", "Sam", "Dave"]
var scores = [10,15,12,15]
I can find the index of the (first) max score (and the winner's name) by using:
let highScore = scores.max()
let winningPlayerIndex = scores.index(of: highScore!)
let winningPlayer = players[winningPlayerIndex!]
This works fine if there is only one player with the highest score but how would I return multiple indices (i.e. 1 and 3 in this example) for all values that are equal to the max value? I need the indices to then map back to the players array to pull out the names of all the players with the highest score. Or is there a better way to do all of this?
The accepted answer doesn't generalize to comparing computed values on the elements. The simplest and most efficient way to get the min/max value and index is to enumerate the list and work with the tuples (offset, element) instead:
struct Player {
let name: String
let stats: [Double]
}
let found = players.enumerated().max(by: { (a, b) in
battingAvg(a.element.stats) < battingAvg(b.element.stats)
})
print(found.element.name, found.offset) // "Joe", 42
In general you shouldn't rely on comparing floating point values by equality and even where you can, if the computation is expensive you don't want to repeat it to find the item in the list.
What you need is to use custom class or structure and make array of it then find max score and after that filter your array with max score.
struct Player {
let name: String
let score: Int
}
Now create array of this Player structure
var players = [Player(name: "Bill", score: 10), Player(name: "Bob", score: 15), Player(name: "Sam", score: 12), Player(name: "Dave", score: 15)]
let maxScore = players.max(by: { $0.0.score < $0.1.score })?.score ?? 0
To get the array of player with max core use filter on array like this.
let allPlayerWithMaxScore = players.filter { $0.score == maxScore }
To get the array of index for player having high score use filter on array like this.
let indexForPlayerWithMaxScore = players.indices.filter { players[$0].score == maxScore }
print(indexForPlayerWithMaxScore) //[1, 3]
To answer just the question in the title -- find the index of the max value in a (single) array:
extension Array where Element: Comparable {
var indexOfMax: Index? {
guard var maxValue = self.first else { return nil }
var maxIndex = 0
for (index, value) in self.enumerated() {
if value > maxValue {
maxValue = value
maxIndex = index
}
}
return maxIndex
}
}
The extension returns nil if the array is empty. Else, it starts by assuming the first value is the max, iterates over all values, updates the index and value to any larger values found, and finally returns the result.
If you have 2 arrays and need to find max score from first one in order to pull the name from second one, then I would recommend you to convert both arrays into one using zip high order func and retrieve the max value from there.
So having your data it will look like this:
let players = ["Bill", "Bob", "Sam", "Dave"]
let scores = [10,15,12,15]
let data = zip(players, scores)
// max score
let maxResult = data.max(by: ({ $0.1 < $1.1 }))?.1 ?? 0
// outputs 15
// leaders
let leaders = data.filter { $0.1 >= maxResult }.map { "\($0.0) - \($0.1)" }
// outputs ["Bob - 15", "Dave - 15"]
You can zip the collection indices with its elements and get the minimum value using collection min method and pass a predicate to compare the elements. Get the result and extract the index of the tuple:
let numbers = [2, 4, 4, 2, 3, 1]
let minIndex = zip(numbers.indices, numbers).min(by: { $0.1 < $1.1 })?.0 // 5
let maxIndex = zip(numbers.indices, numbers).max(by: { $0.1 < $1.1 })?.0 // 1
As an extension where the elements are comparable:
extension Collection where Element: Comparable {
func firstIndexOfMaxElement() -> Index? {
zip(indices, self).max(by: { $0.1 < $1.1 })?.0
}
func firstIndexOfMinElement() -> Index? {
zip(indices, self).min(by: { $0.1 < $1.1 })?.0
}
}
Usage:
numbers.firstIndexOfMinElement() // 5
If you need to find the maximum or minimum properties:
extension Collection {
func firstIndexOfMaxElement<T: Comparable>(_ predicate: (Element) -> T) -> Index? {
zip(indices, self).max(by: { predicate($0.1) < predicate($1.1) })?.0
}
func firstIndexOfMinElement<T: Comparable>(_ predicate: (Element) -> T) -> Index? {
zip(indices, self).min(by: { predicate($0.1) < predicate($1.1) })?.0
}
}
Usage:
struct Product {
let price: Int
}
let products: [Product] = [.init(price: 2),
.init(price: 4),
.init(price: 4),
.init(price: 2),
.init(price: 3),
.init(price: 1),]
let minPrice = products.firstIndexOfMinElement(\.price) // 5
To return the maximum and minimum elements and their indices:
extension Collection where Element: Comparable {
func maxElementAndIndices() -> (indices: [Index], element: Element)? {
guard let maxValue = self.max() else { return nil }
return (indices.filter { self[$0] == maxValue }, maxValue)
}
func minElementAndIndices() -> (indices: [Index], element: Element)? {
guard let minValue = self.min() else { return nil }
return (indices.filter { self[$0] == minValue }, minValue)
}
}
And the corresponding methods to custom structures/classes:
extension Collection {
func maxElementsAndIndices<T: Comparable>(_ predicate: (Element) -> T) -> [(index: Index, element: Element)] {
guard let maxValue = self.max(by:{ predicate($0) < predicate($1)}) else { return [] }
return zip(indices, self).filter { predicate(self[$0.0]) == predicate(maxValue) }
}
func minElementsAndIndices<T: Comparable>(_ predicate: (Element) -> T) -> [(index: Index, element: Element)] {
guard let minValue = self.min(by:{ predicate($0) < predicate($1)}) else { return [] }
return zip(indices, self).filter { predicate(self[$0.0]) == predicate(minValue) }
}
}
Usage:
let maxNumbers = numbers.maxElementAndIndices() // ([1, 2], element 4)
let minNumbers = numbers.minElementAndIndices() // ([5], element 1)
let maxPriceIndices = products.maxElementsAndIndices(\.price) // [(index: 1, element: Product(price: 4)), (index: 2, element: Product(price: 4))]
let minPriceIndices = products.minElementsAndIndices(\.price) // [(index: 5, element: __lldb_expr_22.Product(price: 1))]
There are a couple of ways to solve your problem, you can solve this by saving the indices of scores.max() and iterate through the players list, and also using then zip function:
var max_score = scores.max()
var players_and_score = zip(players, scores)
for player in players_and_score{
if player.1 == max_score{
print(player.0)
}
}

How to search the minimum positive value in an array?

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!
}

Resources