adman
adman

Reputation: 408

Swift - Taking the average of elements of a dictionary

I've looked on StackOverflow but haven't found any places that talk about taking the average of a specific Int or Double within a dictionary. This is my custom dictionary I've created:

var allInformationByDate = [
"1989-06-20": DayData(sales: 0, doorsKnocked: 0, milesWalked: 0.00, hoursWorked: 0.00),
"2016-06-22": DayData(sales: 2, doorsKnocked: 30, milesWalked: 2.00, hoursWorked: 3.00),
"2016-08-16": DayData(sales: 4, doorsKnocked: 30, milesWalked: 10.00, hoursWorked: 0.00)
]

So I would be looking to get the averages for each of these. For example:

var avgSales = average(sales) = 2
var avgDoors = average(doorsKnocked) = 20
var avgMiles = average(milesWalked) = 4.0
var avgHours = average(hoursWorked) = 1.0

Anybody know the syntax? Thanks for helping a noob!

Upvotes: 0

Views: 370

Answers (3)

Bista
Bista

Reputation: 7903

var allInformationByDate = [
    "1989-06-20": DayData(sales: 0, doorsKnocked: 0, milesWalked: 0.00, hoursWorked: 0.00),
    "2016-06-22": DayData(sales: 2, doorsKnocked: 30, milesWalked: 2.00, hoursWorked: 3.00),
    "2016-08-16": DayData(sales: 4, doorsKnocked: 30, milesWalked: 10.00, hoursWorked: 0.00)
]


override func viewDidLoad() {
    super.viewDidLoad()

    var avgSales:Double! = 0.0
    var avgDoorsKnocked:Double! = 0.0
    var avgMilesWalked:Double! = 0.0
    var avgHoursWorked:Double! = 0.0

    let totalDays = Double(allInformationByDate.count)

    for (date, dayData) in allInformationByDate {
        print("date: \(date)")

        avgSales        = avgSales + Double(dayData.sales)
        avgDoorsKnocked = avgDoorsKnocked + Double(dayData.doorsKnocked)
        avgMilesWalked  = avgMilesWalked + dayData.milesWalked
        avgHoursWorked  = avgHoursWorked + dayData.hoursWorked

    }

    avgSales        = avgSales/totalDays
    avgDoorsKnocked = avgDoorsKnocked/totalDays
    avgMilesWalked  = avgMilesWalked/totalDays
    avgHoursWorked  = avgHoursWorked/totalDays

    print("\(avgSales),\(avgDoorsKnocked),\(avgMilesWalked), \(avgHoursWorked)")
}

Upvotes: 1

twiz_
twiz_

Reputation: 1198

The prettiest way is to use a reduce function. You give it a starting value, and a closure that explains how to add the next item in the dictionary. In this case, our starting value would be a tuple of four elements. $0 refers to the item that is being added to, and $1 refers to the item that is added on this iteration. You can also call them $0.0 & $0.1, but I think the previous way looks nicer.

So in the closure below, $0.0, $0.1, $0.2, $0.3 are the items in the tuple, which starts as (0.0,0.0,0.0,0.0), the initial tuple.

(also note that your integer variables will get cut off and not give you the proper average. I'm going to account for that by switching them all to doubles)

let sums = allInformationByDate.reduce((0.0,0.0,0.0,0.0)) {
    ($0.0 + Double($1.value.sales), $0.1 + Double($1.value.doorsKnocked),
    $0.2 + $1.value.milesWalked, $0.3 + $1.value.hoursWorked)
}
let n = allInformationByDate.count
let averages = (sums.0/n, sums.1/n, sums.2/n, sums.3/n) // divide each one by n. you could even give the items in the tuple names here)

edit: quick code change, forgot to specify you are adding from the value in the dictionary.

One more edit: the above assumes that those are also the variable names in the struct or class you're creating in each dictionary value.

Upvotes: 1

Benjamin Lowry
Benjamin Lowry

Reputation: 3799

Creating a function that you are passing an object of DayData into and having that function recognize that is going to be fairly difficult.

What I would recommend is either create four separate functions for each parameter, or have a number system. I will show how you could accomplish both.

This method is assuming that the members of DayData can be publicly accessed.

For just having a function for each parameter, it could look something like this:

func averageSales(dictionary: [String: DayData]) -> Double {
    var total = 0
    for (date, dayData) in dictionary {
         total += dayData.sales
    }
    return = total / dictionary.count
}

Or you could have a similar method which uses a number system (1 for sales, 2 for knocked doors, etc.). It is not a very elegant solution, but you could use it if you like.

func average(dictionary: [String: DayData], parameterNumber: Int) -> Double {
    var total = 0
    if parameterNumber == 1 { //sales
        for (date, dayData) in dictionary {
            total += dayData.sales
        }
        return = total / dictionary.count
    } else if parameterNumber == 2 { //knockedDoors
    ...
}

Upvotes: 0

Related Questions