Search code examples
arraysdictionaryswift3

How to calculate averages by date/month/year from Array in Swift3


I have an Array of Dictionaries.

The Dictionary is like ["date":"2017-07-03T10:12:00", "amount":2.1].

EDIT

Let's say, I have the following array.

let dic1 = ["date":"2017-07-03T10:12:00", "amount":2.0]
let dic2 = ["date":"2017-07-03T11:15:03", "amount":4.0]
let dic3 = ["date":"2017-07-03T17:05:04", "amount":1.0]
let dic4 = ["date":"2017-07-04T09:05:03", "amount":3.0]
let dic5 = ["date":"2017-07-04T12:01:22", "amount":5.0]
let dic6 = ["date":"2017-07-04T19:01:01", "amount":2.0]
let array = [dic1, dic2, dic3, dic4, dic5, dic6]

Now I'm trying to calculate the following result from Array.

"date":"2017-07-03", "average":3.5
"date":"2017-07-04", "average":5.0
"date":"2017-07", "average":2.82..
"date":"2017", "average":2.82..

I could get the average of all data with the following code but I'm stuck now.

let average = array.flatMap({ $0["amount"] as? Double }).reduce(0, +) / Double(array.count)

Solution

  • Suggestion using a custom struct including a date formatter to convert the ISO8601 string to Date

    struct Item {
        let date : Date
        let amount : Double
    
        static let dateFormatter : ISO8601DateFormatter = {
            let formatter = ISO8601DateFormatter()
            formatter.formatOptions = [.withFullDate, .withTime, .withColonSeparatorInTime]
            return formatter
        }()
    
        init(date: String, amount: Double) {
            self.date = Item.dateFormatter.date(from: date)!
            self.amount = amount
        }
    }
    

    Populate the array

    let items = [Item(date : "2017-07-03T10:12:00", amount :2.0),
                 Item(date : "2017-07-03T11:15:03", amount :4.0),
                 Item(date : "2017-07-03T17:05:04", amount :1.0),
                 Item(date : "2017-07-04T09:05:03", amount :3.0),
                 Item(date : "2017-07-04T12:01:22", amount :5.0),
                 Item(date : "2017-07-04T19:01:01", amount :2.0)]
    

    Create a function to take the array and specific date components as parameters

    func average(of items: [Item], matching components: DateComponents) -> Double
    {
        let filteredDates = items.filter { Calendar.current.date($0.date, matchesComponents: components) }
        return filteredDates.map{ $0.amount }.reduce(0.0, +) / Double(filteredDates.count)
    }
    

    Now filter the items by date components and return the average value

    average(of: items, matching: DateComponents(year: 2017, month: 7, day: 3))
    average(of: items, matching: DateComponents(year: 2017, month: 7, day: 4))
    average(of: items, matching: DateComponents(year: 2017, month: 7))
    average(of: items, matching: DateComponents(year: 2017))