Making my function calculate average of array Swift
You have some mistakes in your code:
//You have to set the array-type to Double. Because otherwise Swift thinks that you need an Int-array
var votes:[Double] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
func average(nums: [Double]) -> Double {
var total = 0.0
//use the parameter-array instead of the global variable votes
for vote in nums{
total += Double(vote)
}
let votesTotal = Double(nums.count)
var average = total/votesTotal
return average
}
var theAverage = average(votes)
A small one liner, using old fashioned Objective-C KVC translated in Swift:
let average = (votes as NSArray).value(forKeyPath: "@avg.floatValue")
You can also have the sum:
let sum = (votes as NSArray).value(forKeyPath: "@sum.floatValue")
More on this long forgotten gem : https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueCoding/CollectionOperators.html
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
Simple avarage with filter if needed (Swift 4.2):
let items: [Double] = [0,10,15]
func average(nums: [Double]) -> Double {
let sum = nums.reduce((total: 0, elements: 0)) { (sum, item) -> (total: Double, elements: Double) in
var result = sum
if item > 0 { // example for filter
result.total += item
result.elements += 1
}
return result
}
return sum.elements > 0 ? sum.total / sum.elements : 0
}
let theAvarage = average(nums: items)