RxSwift:在使用 flatMap 和 reduce 时需要帮助
Posted
技术标签:
【中文标题】RxSwift:在使用 flatMap 和 reduce 时需要帮助【英文标题】:RxSwift: Need help using flatMap and reduce 【发布时间】:2016-08-24 09:36:59 【问题描述】:我正在编写一个简单的Diceware 密码生成器来试验 RxSwift。
我正在努力在单独的步骤中使用 flatMap
和 reduce
。
当前代码
我有一个可观察到的wordCount
,它绑定到UIStepper
值,并生成具有给定字数的新密码。
let rawPassword = wordCount
.asObservable()
.map wordCount in
self.rollDice(numberOfDice: wordCount)
.map numbers in wordMap[numbers]!
rollDice
返回一个Observable<String>
(例如:["62345", "23423", "14231", ...]
),然后映射到单词。
rawPassword
是 Observable<Observable<String>>
在本例中,它将是:[["spec", "breed", "plins", "wiry", "chile", "cecil"]]
。
然后我有一个reducedPassword
其中flatMap
和reduce
到一个String
:
let reducedPassword = rawPassword
.flatMap raw in
raw.reduce("") prev, value in
let separator = "-"
return prev == "" ? value : "\(prev)\(separator)\(value)"
这可行,我最终得到了字符串:spec-breed-plins-wiry-chile-cecil
。
问题
现在我想从 UI 更改单词分隔符。当UITextField
中的文本更新时,我只想在rawPassword
上重新应用reduce。
我正在尝试使用combineLatest
将分隔符Observable<String>
与我的Observable<Observable<String>>
rawPassword
结合起来,如下所示:
let reducedPassword = Observable.combineLatest(rawPassword, separator.asObservable()) raw, sep in
raw.reduce("") prev, value in
return prev == "" ? value : "\(prev)\(sep)\(value)"
但是reduce
永远不会触发并且单击步进器不会执行任何操作。
我曾尝试在一个单独的步骤中flatMap
,但随后,combineLatest
我只说了最后一句话。 combineLatest
是正确的方法吗?
【问题讨论】:
为什么不让分隔符可观察?可能保留最后生成的单词... 我的分隔符是Observable<String>
,我只是不知道如何将它与rawPassword
结合起来。
我下面的回答不满足问题吗?
【参考方案1】:
问题
问题在于reducedPassword
(在您的combineLatest
情况下)实际上是Observable<Observable<String>>
。它有助于键入注释,尤其是在学习 RxSwift
以确保事情实际上是您期望的那样时。
let reducedPassword: Observable<Observable<String>> = Observable.combineLatest(rawPassword, separator.asObservable()) // ...
修复
因此,要使修改后的 reducedPassword
正常工作,您需要获取通过 (Observable<String>
) 的元素并用它替换当前的 Observable<Observable<String>>
。所以使用switchLatest()
(这就像在做flatMap $0
):
let reducedPassword = Observable.combineLatest(rawPassword, separator.asObservable())
raw, sep in
raw.reduce("") prev, value in
prev == "" ? value : "\(prev)\(sep)\(value)"
.switchLatest() // equivalent to: .flatMap $0
齐心协力
我将您的所有代码连同修复程序放在一起,可以在没有 UI 的情况下按原样运行:
let disposeBag = DisposeBag()
let wordCount = BehaviorSubject<UInt>(value: 2)
let separator = BehaviorSubject<String>(value: "-")
let wordMap = ["0" : "zero", "1" : "one", "2" : "two", "3" : "three", "4" : "four"]
func rollDice(numberOfDice: UInt) -> Observable<String>
let maxNumber = wordMap.count
return Observable.from(0..<numberOfDice)
.map _ in Int(arc4random()) % maxNumber
.map String($0)
let rawPassword = wordCount
.asObservable()
.map wordCount in
rollDice(numberOfDice: wordCount)
.map numbers in wordMap[numbers]!
let reducedPassword = Observable.combineLatest(rawPassword, separator.asObservable())
raw, sep in
raw.reduce("") prev, value in
prev == "" ? value : "\(prev)\(sep)\(value)"
.switchLatest() // equivalent to: .flatMap $0
reducedPassword
.subscribe(onNext: print($0) )
separator.onNext("||||")
wordCount.onNext(4)
separator.onNext("___")
输出:
零四 零||||两个 两个||||四个||||零||||一个 三___二___二___四
注意事项
我不确定为什么 wordMap
是 [String : String]
而不仅仅是 [String]
。用 wordMap["123"]
而不是 wordMap[123]
来索引地图似乎很奇怪。
如果不使用Observable<Observable<String>>
,而是使用Observable<[String]>
,会更容易实现和理解。这就是你遇到问题的原因,即使你理解了它,它仍然比它需要的复杂。
记住,键入注释你的变量和闭包,以确保你正在做你所期望的。您会更早发现这些问题。
改进的解决方案
考虑到我上面概述的注释,这是另一种编写方式。
let disposeBag = DisposeBag()
let wordCount = BehaviorSubject<UInt>(value: 2)
let separator = BehaviorSubject<String>(value: "-")
let wordMap = ["zero", "one", "two", "three", "four"]
func rollDice(numberOfDice: UInt) -> [Int]
let maxNumber = wordMap.count
return (0..<numberOfDice).map _ in
Int(arc4random()) % maxNumber
let words: Observable<[String]> = wordCount
.asObservable()
.map (count: UInt) -> [String] in
return rollDice(numberOfDice: count)
.map roll in wordMap[roll]
let password: Observable<String> = Observable.combineLatest(words, separator.asObservable())
(words: [String], separator: String) -> String in
words.reduce("") prev, value in
prev == "" ? value : "\(prev)\(separator)\(value)"
password.subscribe(onNext: print($0) )
separator.onNext("||||")
wordCount.onNext(4)
separator.onNext("___")
【讨论】:
以上是关于RxSwift:在使用 flatMap 和 reduce 时需要帮助的主要内容,如果未能解决你的问题,请参考以下文章
使用 if/else 语句返回 observable 不适用于 RxSwift flatMap