Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JadenGeller/942229dd18ad883518b82b6f8c75062b to your computer and use it in GitHub Desktop.
Save JadenGeller/942229dd18ad883518b82b6f8c75062b to your computer and use it in GitHub Desktop.
Denormalizing automatically using Combine
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