Skip to content

Commit

Permalink
Add "sum" function for Sequences with AnyCurrency elements
Browse files Browse the repository at this point in the history
Motivation:

It is common for developers to want a quick sum of values with currencies, to display totals.

To mirror functionality found on Sequence protocols with `max` and `min`, a `sum` with a predicate overload should
be provided.

Modifications:

- Add a `sum` function that starts with a zero value and adds all the elements together
- Add a `sum(where:)` overload that filters the elements by a predicate before summing

Result:

It should be more natural and easy for developers to quickly get a sum of currencies.
  • Loading branch information
Mordil committed Jan 15, 2020
1 parent 8d9b01e commit 87051ef
Show file tree
Hide file tree
Showing 5 changed files with 668 additions and 686 deletions.
49 changes: 49 additions & 0 deletions Sources/Currency/AnyCurrency+Sequence.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Currency open source project
//
// Copyright (c) 2020 Currency project authors
// Licensed under MIT License
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Currency project authors
//
// SPDX-License-Identifier: MIT
//
//===----------------------------------------------------------------------===//

import Foundation

extension Sequence where Element: AnyCurrency {
/// Returns the sum total of all amounts in the sequence.
///
/// For example:
///
/// let amounts: [USD] = [304.98, 19.02]
/// let sumTotal = amounts.sum()
/// print(sumTotal)
/// // prints "324"
///
/// If the sequence has no elements, you will receive a currency with a value of "0".
/// - Complexity: O(*n*) , where *n* is the length of the sequence.
/// - Returns: A currency value representing the sum total of all the amounts in the sequence.
public func sum() -> Element {
return self.reduce(into: .init(.zero), { $0 += $1 })
}

/// Returns the sum total of all amounts in the sequence that satify the given predicate.
/// For example:
///
/// let amounts: [USD] = [304.98, 19.02, 30.21]
/// let sumTotal = amounts.sum(where: { $0.roundedAmount > 20 })
/// print(sumTotal)
/// // prints "335.19"
///
/// - Complexity: O(*n*), where *n* is the length of the sequence.
/// - Parameter predicate: A closure that takes a currency element as its argument
/// and returns a Boolean value that indicates whether the passed element should be included in the sum.
/// - Returns:A currency value representing the sum total of all the amounts in the sequence that satisfies `predicate`.
public func sum(where predicate: (Element) throws -> Bool) rethrows -> Element {
return try self.filter(predicate).sum()
}
}
16 changes: 16 additions & 0 deletions Tests/CurrencyTests/AnyCurrencyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,19 @@ final class AnyCurrencyTests: XCTestCase {
XCTAssertEqual("\(pounds)", "£398.98")
}
}

// MARK: -
// MARK: Sequence<AnyCurrency>

extension AnyCurrencyTests {
func testSequenceSum() {
let amounts = [USD(30.47), -107.8239, 1_203.9832, -504.3982]
XCTAssertEqual(amounts.sum().roundedAmount, 622.23)
}

func testSequenceSum_withPredicate() {
let amounts: [USD] = [304.98, 19.02, 30.21]
let sumTotal = amounts.sum(where: { $0.roundedAmount > 20 })
XCTAssertEqual(sumTotal.roundedAmount, 335.19)
}
}
36 changes: 19 additions & 17 deletions Tests/CurrencyTests/XCTestManifests.swift
Original file line number Diff line number Diff line change
@@ -1,25 +1,11 @@
#if !canImport(ObjectiveC)
import XCTest

extension CurrencyMintTests {
// DO NOT MODIFY: This is autogenerated, use:
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__CurrencyMintTests = [
("testLookupByNum_fails", testLookupByNum_fails),
("testLookupByNum_passes", testLookupByNum_passes),
("testLookupByNum_withAmount", testLookupByNum_withAmount),
("testLookupByString_fails", testLookupByString_fails),
("testLookupByString_passes", testLookupByString_passes),
("testLookupByString_withAmount", testLookupByString_withAmount),
]
}

extension MoneyTests {
extension AnyCurrencyTests {
// DO NOT MODIFY: This is autogenerated, use:
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__MoneyTests = [
static let __allTests__AnyCurrencyTests = [
("testAddition", testAddition),
("testComparable", testComparable),
("testDescription", testDescription),
Expand All @@ -29,6 +15,8 @@ extension MoneyTests {
("testInit", testInit),
("testMinorUnits", testMinorUnits),
("testMultiplication", testMultiplication),
("testSequenceSum", testSequenceSum),
("testSequenceSum_withPredicate", testSequenceSum_withPredicate),
("testStringInterpolation_customFormatter", testStringInterpolation_customFormatter),
("testStringInterpolation_customLocale", testStringInterpolation_customLocale),
("testStringInterpolation_defaultFormatter", testStringInterpolation_defaultFormatter),
Expand All @@ -38,10 +26,24 @@ extension MoneyTests {
]
}

extension CurrencyMintTests {
// DO NOT MODIFY: This is autogenerated, use:
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__CurrencyMintTests = [
("testLookupByNum_fails", testLookupByNum_fails),
("testLookupByNum_passes", testLookupByNum_passes),
("testLookupByNum_withAmount", testLookupByNum_withAmount),
("testLookupByString_fails", testLookupByString_fails),
("testLookupByString_passes", testLookupByString_passes),
("testLookupByString_withAmount", testLookupByString_withAmount),
]
}

public func __allTests() -> [XCTestCaseEntry] {
return [
testCase(AnyCurrencyTests.__allTests__AnyCurrencyTests),
testCase(CurrencyMintTests.__allTests__CurrencyMintTests),
testCase(MoneyTests.__allTests__MoneyTests),
]
}
#endif
Loading

0 comments on commit 87051ef

Please sign in to comment.