@@ -54,6 +54,59 @@ public extension Array {
5454
5555 return consolidated ( by: { $0 [ keyPath: keyPath] == $1 [ keyPath: keyPath] } , using: consolidating)
5656 }
57+
58+
59+ /// Consolidates (reduces) an array of `Elements`, by a `KeyPath` using a given closure, into a single element.
60+ ///
61+ /// ### Example
62+ ///
63+ /// let allTaxes = [
64+ /// TaxAmount(name: "Import Tax", amount: 3.00),
65+ /// TaxAmount(name: "Import Tax", amount: 2.30)
66+ /// ]
67+ ///
68+ /// // The next line would result in TaxAmount(name: "Import Tax", amount: 5.30)
69+ ///
70+ /// let consolidatedTaxes = allTaxes.consolidated(by: \.name) {
71+ /// TaxAmount(tax: $0.name, amount: $0.amount + $1.amount)
72+ /// }
73+ ///
74+ /// Since the `TaxAmount` type is consolidated by name, the two entries for _"Import Tax"_ have been consolidated
75+ /// into a single `TaxAmount` where their `amount` values have been added.
76+ ///
77+ ///
78+ /// let allTaxes = [
79+ /// TaxAmount(name: "Import Tax", amount: 3.00),
80+ /// TaxAmount(name: "Sales Tax", amount: 1.75),
81+ /// TaxAmount(name: "Import Tax", amount: 2.30)
82+ /// ]
83+ ///
84+ /// // The next line would throw
85+ ///
86+ /// let consolidatedTaxes = allTaxes.consolidated(by: \.name) {
87+ /// TaxAmount(tax: $0.name, amount: $0.amount + $1.amount)
88+ /// }
89+ ///
90+ /// Since the `TaxAmount` entries would consolidate to two elements (Import Tax and Sales Tax) the example above would throw
91+ /// the error `ConsolidationError.couldNotBeConolidatedIntoSingleElement`.
92+ ///
93+ /// - Parameters:
94+ /// - keyPath: The key path to the property to use as a consolidation group.
95+ /// - consolidating: The closure used to consolidate two `Element` values into a single `Element` value.
96+ ///
97+ /// - Returns: A single element representing the consolidation by a `KeyPath` using the given `consolidating` closure.
98+ /// - Throws: `ConsolidationError.couldNotBeConolidatedIntoSingleElement` error if the array does not consolidate into a single element.
99+ @inlinable
100+ func consolidatedIntoSingle< GroupType: Hashable > ( by keyPath: KeyPath < Element , GroupType > , using consolidating: Consolidate ) throws -> Element {
101+
102+ let consolidatedArray = consolidated ( by: keyPath, using: consolidating)
103+
104+ guard consolidatedArray. count == 1 , let consolidatedSingle = consolidatedArray. first else {
105+ throw ConsolidationError . couldNotBeConolidatedIntoSingleElement
106+ }
107+
108+ return consolidatedSingle
109+ }
57110
58111 /// Consolidates (reduces) an array of `Elements` grouped by the result of a closure
59112 /// combined using another closure.
@@ -102,6 +155,14 @@ public extension Array {
102155 return result + [ element]
103156 }
104157 }
158+
159+ // MARK: Errors
160+
161+ /// Errors that can occur during consolidation
162+ enum ConsolidationError : Error {
163+ /// Thrown when attempting to concolidate to a single element, but the collection can not be consolidated to a single element.
164+ case couldNotBeConolidatedIntoSingleElement
165+ }
105166}
106167
107168/// A type implementing `Consolidatable` allows an array of its values to be combined by
0 commit comments