在Swift数组中查找元素的总和

295
什么是在Swift中查找整数数组总和的最简单(最佳)方法? 我有一个名为multiples的数组,我想知道它们的总和。
16个回答

577

这是我能找到的最简单/最短的方法。

Swift 3和Swift 4:

let multiples = [...]
let sum = multiples.reduce(0, +)
print("Sum of Array is : ", sum)

Swift 2:

let multiples = [...]
sum = multiples.reduce(0, combine: +)

更多信息:

这里使用了数组的reduce方法(文档在这里),它允许您“通过递归应用提供的闭包将元素集合减少为单个值”。我们将0作为初始值,并且使用闭包{ $0 + $1 }。当然,我们可以简化为一个加号,因为这就是Swift的做法。


60
谢谢您的答复。它非常有效,我只想添加我的示例代码,其中包含自定义类的数组:let totalSum = self.cheques.reduce(0) { $0 + $1.amount} - Libor Zapletal
2
回答很好,但缺少参数名称“combine”。应为multiples.reduce(0, combine: +) - Evgenii
多维(比如2D或3D)数组中元素的总和是多少? - SwiftMatt
2
它比for循环更快吗? - lorenzo
2
@SwiftMatt 首先尝试使用flatMap将其展平为单维数组。multiArray.flatMap{$0}.reduce(0, combine: +) - Samah
3
在iPhone 7上用最新的Swift和XCode进行测试(一个包含1,000,000个UInt32随机值且限制在256以防止UInt32溢出的简单数组)有趣地显示'for'循环比使用其他方法略快(1.941秒对2.137秒),尽管这种优势在只有100,000个数值时并不存在(每个数值0.23秒)。在处理32MB数组时,我认为代码清晰易懂比非常小的性能代价更重要。 - Tom Dibble

140

Swift 3+的一行代码来汇总对象的属性

var totalSum = scaleData.map({$0.points}).reduce(0, +)

在我的自定义对象scaleData中,points是我尝试减少的属性。


2
这将不必要地两次迭代整个集合。 - Leo Dabus
4
您可以通过 scaleData.reduce(0) { $0 + $1.points } 实现这一点。 - Climbatize

51
在Swift 4中,您还可以将序列元素限制为Numeric协议,以返回序列中所有元素的总和,如下所示。
extension Sequence where Element: Numeric {
    /// Returns the sum of all elements in the collection
    func sum() -> Element { return reduce(0, +) }
}

编辑/更新:

Xcode 10.2 • Swift 5或更高版本

我们可以将序列元素简单地约束到新的AdditiveArithmetic协议,以返回集合中所有元素的总和。

extension Sequence where Element: AdditiveArithmetic {
    func sum() -> Element {
        return reduce(.zero, +)
    }
}

Xcode 11 • Swift 5.1或更高版本

extension Sequence where Element: AdditiveArithmetic {
    func sum() -> Element { reduce(.zero, +) }
}

let numbers = [1,2,3]
numbers.sum()    // 6

let doubles = [1.5, 2.7, 3.0]
doubles.sum()    // 7.2

为了对自定义对象的属性求和,我们可以扩展Sequence以采用一个谓词来返回符合AdditiveArithmetic的值。
extension Sequence  {
    func sum<T: AdditiveArithmetic>(_ predicate: (Element) -> T) -> T { reduce(.zero) { $0 + predicate($1) } }
}

使用方法:

struct Product {
    let id: String
    let price: Decimal
}

let products: [Product] = [.init(id: "abc", price: 21.9),
                           .init(id: "xyz", price: 19.7),
                           .init(id: "jkl", price: 2.9)
]

products.sum(\.price)  // 44.5

3
好的,很棒的解决方案! - sabiland
1
对我来说最好的答案! - FrugalResolution

31

Swift3现在变成了:

let multiples = [...]
sum = multiples.reduce(0, +)

16

Swift 4示例

class Employee {
    var salary: Int =  0
    init (_ salary: Int){
        self.salary = salary
    }
}

let employees = [Employee(100),Employee(300),Employee(600)]
var sumSalary = employees.reduce(0, {$0 + $1.salary}) //1000

非常有帮助,因为它包括了一个计算对象数组中算术属性之和的示例,这正是我需要做的。 - Carl Smith

13

这也可以工作:

let arr = [1,2,3,4,5,6,7,8,9,10]
var sumedArr = arr.reduce(0, combine: {$0 + $1})
print(sumedArr)

结果将是:55


11

Swift 3

如果您有一个泛型对象数组,并且想要对某个对象属性进行求和,则:

class A: NSObject {
    var value = 0
    init(value: Int) {
       self.value = value
    }
}

let array = [A(value: 2), A(value: 4)]      
let sum = array.reduce(0, { $0 + $1.value })
//                           ^       ^
//                        $0=result  $1=next A object
print(sum) // 6 

尽管使用较短的形式,但在许多情况下,您可能更喜欢经典的for循环:

let array = [A(value: 2), A(value: 4)]
var sum = 0
array.forEach({ sum += $0.value}) 
// or
for element in array {
   sum += element.value
}

1
你好 Luca,这是我的解决方案:array.map({$0.value}).reduce(0, +)。 - Ivailo Kanev
2
这样做会写更多的代码并增加复杂性,那么为什么呢? - Luca Davanzo

8

Swift 3, 4和5

使用reduce:

let totalAmount = yourTransactionsModelArray.reduce(0) { $0 + $1.amount}

为了易于理解,使用老式方法:

for (var i = 0; i < n; i++) {
 sum = sum + Int(multiples[i])!
}

//其中n为数组中元素的数量


2
请注意,在Swift 3中,这不再有效,因为语言中已删除了C样式的循环。改用for-in循环,Swift会在内部跟踪索引。 - donarb
1
那样是行不通的。你必须先将sum初始化为0。reduce的美妙之处在于它提醒你考虑初始值。 - MarkAurelius
var sum = 0; for n in multiples { sum += n } 虽然我会使用 reduce。 - JeremyP
@naishta 已经提到,她写的 for 循环仅用于理解目的。 - Anand

3
一种可能的解决方案:为其定义一个前缀运算符。 就像APL中的reduce "+/"运算符一样(例如GNU APL)
这里有一种略有不同的方法。
使用协议和通用类型使我们能够在Double、Float和Int数组类型上使用此运算符。
protocol Number 
{
   func +(l: Self, r: Self) -> Self
   func -(l: Self, r: Self) -> Self
   func >(l: Self, r: Self) -> Bool
   func <(l: Self, r: Self) -> Bool
}

extension Double : Number {}
extension Float  : Number {}
extension Int    : Number {}

infix operator += {}

func += <T:Number> (inout left: T, right: T)
{
   left = left + right
}

prefix operator +/ {}

prefix func +/ <T:Number>(ar:[T]?) -> T?
{
    switch true
    {
    case ar == nil:
        return nil

    case ar!.isEmpty:
        return nil

    default:
        var result = ar![0]
        for n in 1..<ar!.count
        {
            result += ar![n]
        }
        return result
   }
}

使用方式如下:

let nmbrs = [ 12.4, 35.6, 456.65, 43.45 ]
let intarr = [1, 34, 23, 54, 56, -67, 0, 44]

+/nmbrs     // 548.1
+/intarr    // 145

(已更新至Swift 2.2,测试于Xcode版本7.3)


其中一个问题是,通常情况下(至少在处理大型数组时),您需要的总和与组件不同。例如,如果您有一百万个UInt32,范围从0到2 ^ 32-1(例如,由arc4random()填充),将它们相加将每次都会使您的UInt32“sum”值溢出。 - Tom Dibble

3

Swift 3.0

我也遇到了同样的问题,在苹果公司的文档中找到了解决方法。

let numbers = [1, 2, 3, 4]
let addTwo: (Int, Int) -> Int = { x, y in x + y }
let numberSum = numbers.reduce(0, addTwo)
// 'numberSum' == 10

但是在我的情况下,我有一个对象列表,然后我需要转换我的列表值:

let numberSum = self.list.map({$0.number_here}).reduce(0, { x, y in x + y })

这对我很有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接