> For the complete documentation index, see [llms.txt](https://lochiwei.gitbook.io/ios/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lochiwei.gitbook.io/ios/swift/collections/sequence/reduce-vs-reduceinto.md).

# reduce() vs. reduce(into:)

{% tabs %}
{% tab title="📗 參考" %}

* [x] Advanced Swift, p.37
  {% endtab %}

{% tab title="🗣 討論" %}

* [Using reduce(into:\_:) to filter adjacent equal elements](https://stackoverflow.com/questions/47613651/using-reduceinto-to-filter-adjacent-equal-elements)
  {% endtab %}

{% tab title="📘 手冊" %}

* Swift ⟩ [Sequence](https://developer.apple.com/documentation/swift/sequence) ⟩
  * [.reduce()](https://developer.apple.com/documentation/swift/sequence/2907677-reduce)
  * [.reduce(into:\_:)](https://developer.apple.com/documentation/swift/sequence/3128812-reduce)
* [SwiftUI](https://developer.apple.com/documentation/swiftui)  ⟩  [Drawing & Animation](https://developer.apple.com/documentation/swiftui/drawing-and-animation)  ⟩ [AnyTransition](https://developer.apple.com/documentation/swiftui/anytransition)
  {% endtab %}

{% tab title="👥 相關" %}

* [seq.reduce(\_:\_:)](/ios/swift/collections/sequence/seq.reduce-_-_.md)
* [seq.reduce(into:\_:)](/ios/swift/collections/sequence/seq.reduce-into-_.md)
  {% endtab %}
  {% endtabs %}

{% hint style="info" %}

* 參數不同：\ <mark style="color:purple;">`reduce(into:_:)`</mark>有 <mark style="color:red;">**inout parameter**</mark>，<mark style="color:purple;">`reduce(_:_:)`</mark>沒有，所以當它將之前累積的結果傳入 <mark style="color:purple;">nextPartialResult</mark> 並需要<mark style="color:red;">**更新**</mark>新的累積結果時，就必須<mark style="color:red;">**複製**</mark> (<mark style="color:orange;">**copy-on-write**</mark>) 之前的結果，所以如果 <mark style="color:purple;">Result</mark> 是一個大型的 <mark style="color:purple;">Array</mark> 或 <mark style="color:purple;">Dictionary</mark> 之類的型別，就會**對程式的效率產生衝擊**。
* 回傳方式不同：

  <mark style="color:purple;">`reduce(_:_:)`</mark> 是將 <mark style="color:purple;">nextPartialResult</mark> <mark style="color:red;">**最後的 return value**</mark> 作為整個方法的**回傳值**。<mark style="color:purple;">`reduce(into:_:)`</mark> 則是用 <mark style="color:purple;">updateAccumulatingResult</mark> 持續對 <mark style="color:purple;">partialResult</mark> 做**更新**，然後以 <mark style="color:purple;">partialResult</mark> <mark style="color:red;">**最後的值**</mark>作為**回傳值**。
  {% endhint %}

{% hint style="danger" %}
所以使用<mark style="color:purple;">`reduce(into:_:)`</mark>時要注意：「一定要透過 <mark style="color:purple;">updateAccumulatingResult</mark> 來更新 <mark style="color:purple;">partialResult</mark>」，否則<mark style="color:red;">**回傳值**</mark>會是**原來的** <mark style="color:purple;">initialResult</mark>:exclamation:
{% endhint %}

```swift
// `reduce(_:_:)`
func reduce<Result>(
    _ initialResult    : Result, 
    // ⭐️ combine function (with return value)
    _ nextPartialResult: (Result, Element) throws -> Result
) rethrows -> Result

// `reduce(into:_:)`
func reduce<Result>(
    into initialResult: Result, 
    _ updateAccumulatingResult: (
        _ partialResult: inout Result,    // ⭐️ inout parameter
        Self.Element    
    ) throws -> ()                        // ⭐️ no return value
) rethrows -> Result
```

{% hint style="warning" %}
.[reduce(into:)](https://developer.apple.com/documentation/swift/array/3126956-reduce) is **preferred** over [reduce()](https://developer.apple.com/documentation/swift/array/2298686-reduce) for **efficiency** when the <mark style="color:red;">**result**</mark> is a **copy-on-write** type, for example an <mark style="color:red;">**Array**</mark> or a <mark style="color:red;">**Dictionary**</mark>. :point\_right: [.reduce(into:\_:)](https://developer.apple.com/documentation/swift/sequence/3128812-reduce)
{% endhint %}

## Example

{% tabs %}
{% tab title="📘 手冊" %}

* View ⟩ [Graphics and Rendering](https://developer.apple.com/documentation/swiftui/view-graphics-and-rendering) ⟩
  * [.transition(\_:)](https://developer.apple.com/documentation/swiftui/view/transition\(_:\))
    {% endtab %}

{% tab title="📗 參考" %}

* [ ] Pavel Zak ⟩  [Mastering transitions in SwiftUI](https://nerdyak.tech/development/2020/10/12/transitions-in-swiftui.html)
  {% endtab %}

{% tab title="👥 相關" %}

* [AnyTransition.combined()](/ios/swiftui/anim/transitions/anytransition/anytransition.combined.md)
  {% endtab %}
  {% endtabs %}

我們用合併 SwiftUI 中的 transitions 來舉例，說明這兩者的用法有哪些不同。在合併 SwiftUI 中的 transitions 時，使用 SwiftUI 原有的語法，顯得太過冗長：

```swift
AnyTransition
    .move(edge: .leading)
    .combined(with: .opacity)
    .combined(with: .scale)
```

若使用 .[reduce()](https://developer.apple.com/documentation/swift/array/2298686-reduce) 或 .[reduce(into:)](https://developer.apple.com/documentation/swift/array/3126956-reduce)，則可以將語法變為更精簡：

```swift
// 無法自行推斷 (infer) AnyTransition 型別的時候：
AnyTransition(.move(edge: .leading), .opacity, .scale)

// 可以自行推斷 AnyTransition 型別的時候：
.combined(.move(edge: .leading), .opacity, .scale)
```

前提是要先加入以下的 extension：

```swift
extension AnyTransition {
    
    // ⭐️ combining transitions
    // AnyTransition([.scale, .opacity, ...]) ........ (1)
    init(_ transitions: [AnyTransition]) {
        // use reduce() or reduce(into:)
    }
    
    // 讓可以使用的語法更豐富，在可以使用 AnyTransition 的地方，直接用：
    // .combined(.move, .scale, ...)
    public static func combined(_ transitions: AnyTransition...) -> AnyTransition {
        .init(transitions)
    }
}

extension AnyTransition: ExpressibleByArrayLiteral {
    // convenience init
    // AnyTransition(.move, .scale, .opacity, ...)
    public init(arrayLiteral elements: AnyTransition...) {
        self.init(elements)    // 使用 (1)
    }
}
```

至於用 .reduce() 還是 .reduce(into:)？它們之間有何不同，請看下面的程式碼：

## .reduce()

```swift
init(_ transitions: [AnyTransition]) {
    let intialValue = AnyTransition.identity
    // ⭐️ 使用 `reduce(_:_)`
    self = transitions.reduce(intialValue) { (result, transition) in
        // ⭐️ 下面的結果會「自動成為新的 `result`」，不需手動改寫 `result`
        //    所以官方稱這個部分的 closure 為：`nextPartialResult`。
        result.combined(with: transition)    // ⭐️ implicit return
    }
}
```

## .reduce(into:)

```swift
init(_ transitions: [AnyTransition]) {
    var intialValue = AnyTransition.identity
    // ⭐️ 使用 `reduce(into:_:)`
    self = transitions.reduce(into: intialValue) { (result, transition) in
        // ⭐️ 記得手動改寫 `result` 變數，否則效果並不會累加。
        //    因為如果我們不寫 `result = ...`，這時 `result` 變數
        //    並不會自動改變，所以官方稱這個部分為 `updateAccumulatingResult`
        //    而不是 `nextPartialResult`。
        result = result.combined(with: transition)    // ⭐️ no return value
    }
}
```
