Search code examples
arraysfunctionswiftswift-playground

Making my function calculate average of array Swift


I want my function to calculate the average of my Double type array. The array is called "votes". For now, I have 10 numbers.

When I call the average function to get the average of the array votes, it doesn't work.

Here's my code:

var votes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

func average(nums: Double...) -> Double {
    var total = 0.0
    for vote in votes {
        total += vote
    }
    let votesTotal = Double(votes.count)
    var average = total/votesTotal
    return average
}

average[votes]

How do I call the average here to get the average?


Solution

  • You should use the reduce method to sum your sequence elements as follow:

    Xcode Xcode 10.2+ • Swift 5 or later

    extension Sequence where Element: AdditiveArithmetic {
        /// Returns the total sum of all elements in the sequence
        func sum() -> Element { reduce(.zero, +) }
    }
    

    extension Collection where Element: BinaryInteger {
        /// Returns the average of all elements in the array
        func average() -> Element { isEmpty ? .zero : sum() / Element(count) }
        /// Returns the average of all elements in the array as Floating Point type
        func average<T: FloatingPoint>() -> T { isEmpty ? .zero : T(sum()) / T(count) }
    }
    

    extension Collection where Element: BinaryFloatingPoint {
        /// Returns the average of all elements in the array
        func average() -> Element { isEmpty ? .zero : sum() / Element(count) }
    }
    

    let votes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    let votesTotal = votes.sum()                       // 55
    let votesAverage = votes.average()                 // 5
    let votesDoubleAverage: Double = votes.average()   // 5.5
    

    If you need to work with Decimal types its total sum it is already covered by the AdditiveArithmetic protocol extension method, so you only need to implement the average:

    extension Collection where Element == Decimal {
        func average() -> Decimal { isEmpty ? .zero : sum() / Decimal(count) }
    }
    


    If you need to sum a certain property of a custom structure we can extend Sequence and create a method that takes a KeyPath as argument to calculate its sum:

    extension Sequence  {
        func sum<T: AdditiveArithmetic>(_ predicate: (Element) -> T) -> T {
            reduce(.zero) { $0 + predicate($1) }
        }
    }
    

    Usage:

    struct User {
        let name: String
        let age: Int
    }
    
    let users: [User] = [
        .init(name: "Steve", age: 45),
        .init(name: "Tim", age: 50)]
    
    let ageSum = users.sum(\.age) // 95
    

    And extend collection to calculate its average:

    extension Collection {
        func average<T: BinaryInteger>(_ predicate: (Element) -> T) -> T {
            sum(predicate) / T(count)
        }
        func average<T: BinaryInteger, F: BinaryFloatingPoint>(_ predicate: (Element) -> T) -> F {
            F(sum(predicate)) / F(count)
        }
        func average<T: BinaryFloatingPoint>(_ predicate: (Element) -> T) -> T {
            sum(predicate) / T(count)
        }
        func average(_ predicate: (Element) -> Decimal) -> Decimal {
            sum(predicate) / Decimal(count)
        }
    }
    

    Usage:

    let ageAvg = users.average(\.age)                 // 47
    let ageAvgDouble: Double = users.average(\.age)   // 47.5