Created
March 7, 2020 04:32
-
-
Save JadenGeller/942229dd18ad883518b82b6f8c75062b to your computer and use it in GitHub Desktop.
Denormalizing automatically using Combine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Combine | |
extension Publishers { | |
typealias Pair<Upstream> = Publishers.Map<Publishers.Scan<Upstream, (Upstream.Output?, Upstream.Output?)>, (Upstream.Output?, Upstream.Output)> where Upstream: Publisher | |
} | |
extension Publisher { | |
func pair() -> Publishers.Pair<Self> { | |
scan((nil as Output?, nil as Output?), { previousPair, value in | |
(previousPair.1, value) | |
}).map({ pair in | |
(pair.0, pair.1!) | |
}) | |
} | |
} | |
extension Publishers { | |
typealias Difference<Upstream> = Publishers.CompactMap<Pair<Upstream>, CollectionDifference<Upstream.Output.Element>> | |
where Upstream: Publisher, Upstream.Output: BidirectionalCollection | |
} | |
extension Publisher where Output: BidirectionalCollection, Output.Element: Equatable { | |
func difference() -> Publishers.Difference<Self> { | |
pair().compactMap({ pair in | |
guard case let (previous?, next) = pair else { return nil } | |
return next.difference(from: previous) | |
}) | |
} | |
} | |
extension Publisher where Output: BidirectionalCollection, Output.Element: Equatable { | |
func difference(by areEquivalent: @escaping (Output.Element, Output.Element) -> Bool) -> Publishers.Difference<Self> { | |
pair().compactMap({ pair in | |
guard case let (previous?, next) = pair else { return nil } | |
return next.difference(from: previous, by: areEquivalent) | |
}) | |
} | |
} | |
extension CollectionDifference.Change { | |
var element: ChangeElement { | |
switch self { | |
case .insert(_, let element, _): | |
return element | |
case .remove(_, let element, _): | |
return element | |
} | |
} | |
} | |
struct Person: Equatable { | |
var name: String | |
var age: Int | |
var blanketCount: Int | |
} | |
class Database { | |
@Published | |
var users: [Person] = [] | |
var totalBlanketCount = 0 | |
} | |
let database = Database() | |
database.$users.difference().map({ | |
$0.map({ | |
switch $0 { | |
case .insert(_, let person, _): | |
return person.blanketCount | |
case .remove(_, let person, _): | |
return -person.blanketCount | |
} | |
}).reduce(0, +) | |
}).scan(0, +).assign(to: \Database.totalBlanketCount, on: database) | |
database.users.append(Person(name: "Jaden", age: 24, blanketCount: 2)) | |
database.users.append(Person(name: "Sawyer", age: 23, blanketCount: 3)) | |
database.users.sort(by: { left, right in left.age < right.age }) | |
print(database.totalBlanketCount) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment