如何在不覆盖以前数据的情况下使用 rxSwift 压缩、合并或连接?

Posted

技术标签:

【中文标题】如何在不覆盖以前数据的情况下使用 rxSwift 压缩、合并或连接?【英文标题】:How do I zip, merge or concat using rxSwift without overriding previous data? 【发布时间】:2020-10-27 19:08:34 【问题描述】:

我想将三个 Observable<[Recipe]> 流合并为一个 Observable<[Recipe]>

let breakfast = Database.database().reference().rx.fetchFireBaseData(recipeType: .breakfast)
    .map  $0.filter  UserDefaults.standard.favoriteRecipes.contains($0.fbKey) 
        
let dessert = Database.database().reference().rx.fetchFireBaseData(recipeType: .dessert)
    .map  $0.filter  UserDefaults.standard.favoriteRecipes.contains($0.fbKey) 

let cookies = Database.database().reference().rx.fetchFireBaseData(recipeType: .cookies)
    .map  $0.filter  UserDefaults.standard.favoriteRecipes.contains($0.fbKey) 

如果我使用Observable.zip(breakfast, dessert, cookies).do(onNext: [weak self] value in... 我得到([Recipe], [Recipe], [Recipe])

如果我使用Observable.merge(breakfast, dessert, cookies).do(onNext: [weak self] value in... 我轮流获得流,这意味着最后一个完成的会覆盖前两个。

如果我使用Observable.concat(breakfast, dessert, cookies).do(onNext: [weak self] value in... 我得到了与merge 相同的行为。

那么如何将三个流合并为一个而不让它们相互覆盖呢?

为了更好地了解我想要做什么:

return Observable.merge(breakfast, cookies, dessert).do(onNext:  [weak self] value in
    self?.content.accept(value.compactMap  FavoritesCollectionViewCellViewModel(favorite: $0))
)

我想将accept 的所有组合流映射到behaviorRelay,映射到cell 数据。然后content 中继是boundcollectionView。在观看collectionView 时,使用concat 我可以简要地看到第一个和第二个流,然后将它们替换为最后一个流。

【问题讨论】:

【参考方案1】:

您可能不知道,但是 combineLatest(和 zip)有一个用于操作输入的最终参数。所以你可以这样做:

let allRecipes = Observable.combineLatest(breakfast, dessert, cookies)  $0 + $1 + $2 

相当于:

Observable.combineLatest(breakfast, dessert, cookies)
        .map  $0.0 + $0.1 + $0.2 

这与您最终得到的基本相同,只是您使用 zip 而不是 combineLatest

使用zip 假设所有三个 Observable 将发出完全相同数量的元素。通过使用 combineLatest,您假设每个 Observable 至少发出一个元素,但它们不必都发出相同的数字。

如果您想避免这种假设,请为每个来源添加.startWith

Observable.combineLatest(
    breakfast.startWith([]),
    dessert.startWith([]),
    cookies.startWith([])
)  $0 + $1 + $2 

通过上述方法,只要任何一个源 observable 发出,您就会在输出中得到一些东西,并且每次其他源发出时都会得到另一个更大的数组。

请注意,在上述所有内容(包括您的答案)中,任何一个源 observable 中的错误都会导致整个事情出错。如果你想避免这种情况,你必须在每个源 Observable 上捕获错误。该代码的确切外观取决于您要如何处理这些错误。

【讨论】:

这是一个非常好的解释,非常感谢@Daniel T。【参考方案2】:

好的,不知道这是否是最漂亮的解决方法,但我使用了zip 然后只是在compactMap 中将数组相互添加

return Observable.zip(breakfast, dessert, cookies)
    .compactMap  $0.0 + $0.1 + $0.2 
    .do(onNext:  [weak self] value in
        self?.content.accept(value.compactMap  FavoritesCollectionViewCellViewModel(favorite: $0))
)

【讨论】:

以上是关于如何在不覆盖以前数据的情况下使用 rxSwift 压缩、合并或连接?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不覆盖之前数据的情况下创建多个 java txt 文件? [复制]

如何在不覆盖的情况下向 Firebase Firestore 添加值?

如何在不覆盖现有记录的情况下将新行添加到数据表

如何在不覆盖当前数据的情况下更新 Mongoose 中的混合类型字段?

如何在不覆盖数据源参数的情况下将附加参数传递给剑道模板

ASP.NET 如何在不删除以前数据的情况下向数据库单元添加其他数据