Sequence contains more than one element
pwittchen opened this issue · comments
Hi,
I'm getting the following error with RxSwift: Sequence contains more than one element
My code looks as follows:
Observable<Int>
.interval(RxTimeInterval.seconds(1), scheduler: Schedulers.serialInteractive)
.filter { ... }
.flatMap { /* here I'm returning Single */ }
.map { ... }
.map { ... }
.flatMap { /* here I'm returning Completable */ }
.subscribe() // <-- error is returned here in onError function
Moreover, inside first Single I'm calling:
Observable
.merge(single1.asObservable(), single2.asObservable())
.map { ... }
.map { ... }
.do(onNext: { _ ... })
.asSingle()
I don't know, why am I getting this error. Everything compiles, app is running, my tests are passing, but I'm getting this error anyway and app does not work as expected. Maybe there's a problem with different observable types (Observable/Single/Completable) and I need to convert all of them to Observable to make it work? I appreciate any help.
Regards,
Piotr
Hey Piotr,
Unfortunately no way for me to check it with this theoretical code. Can you please create a code path that reproduces your issue in a small project?
Everything inside filter
/map
etc can just return simple Single and Completable, if you need to make it compile.
Thanks!
That last bit of code Observable.merge(single1.asObservable(), single2.asObservable()).asSingle()
can't possibly work. The merge will emit two values... Maybe you meant to use Single.zip
Thank you for your responses. I provided pseudo-code because real code contains project specific stuff and it's part of the proprietary software, so I don't want to share it here. Maybe later I'll try to prepare isolated code sample with reproduction of this problem.
@danielt1263 Probably zip
is not the operator, which will be the best here. streams single1
and single2
takes data from different sources, but they generate output stream of the same type and I want to join (merge) them together. I don't to create the third type out of these two. Maybe I'll try to check other joining rx operators for this case.
If you don't think zip
is what you want that's fine. That doesn't change the fact that Observable.merge(single1.asObservable(), single2.asObservable()).asSingle()
can't possibly work because the merge
will emit two next events which is explicitly forbidden from a Single. That's why you are getting the error you see.
If you don't want zip
then maybe remove the toSingle()
because it's not a Single.
@pwittchen Indeed Daniel is right here, seems there is a misunderstanding
Single = one and only one value
Merge = Possibly one or two values (depending on if both "Single"s fire at this point)
It doesn't fit together. I think you have a regular Observable on your hands.
Thanks for your responses. I'll provide more detailed explanation here because probably something is wrong with my logic.
In the input I'm getting two streams (they can be Observable<[Event]>
or Single<[Event]>
), then I want to merge these two streams together and after that create only one a single object, which contains an array consisting of the items collected and transformed from these two streams. I'm doing this in the first map
operator and then I'm sorting events in the second map
operator.
You might be able to leverage toArray
:
RxSwift/RxSwift/Observables/ToArray.swift
Lines 19 to 22 in 1a1fa37
How can I leverage toArray
? As far as I see toArray just generates Single<[Element]>
and I'm able to do that. I see that RxSwift does not have Single.merge
and that would be the function I probably need here because I want to merge two Singles/Observables and have Single on the output.
I'm sorry, I don't think you entirely understand what merge does.
Merge takes multiple observables and interleaves their values.
It's inherently impossible merging two Singles, because that would create something that emits more than a single value - which is not a Single.
toArray()
will emit an Observable's items until completion and emit them as a Single with a single value that is an array of Element
s.
Ok, I'll try to rewrite this code somehow. Regarding the fact that merging Single is impossible, then probably there are differences between several Rx implementations on the conceptual level. Have a look at RxJava (the most popular Rx implementation). There is even dedicated SingleMergeTest
class: https://github.com/ReactiveX/RxJava/blob/5bb119b1489afe4fbc5bb51b828be95971ee8f1d/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleMergeTest.java
The RxSwift implementation follows the standard defined in ReactiveX. Learn more about merge
here.
@danielt1263 here (same page you linked, but in different section): https://reactivex.io/documentation/single.html you can read about "Composition via Single Operators" and there's merge
operator.
Anyway, I'm not here to debate on the correctness and standards of RxSwift. I just want to solve my problem :). I want to merge two Singles (they can be Observables too) containing an array of objects and return a Single on the output containing transformed and sorted data from these two Singles/Observables without error from the first post. If you could give me an example, how it should be done correctly, I'll appreciate that.
Okay... Let's talk about this conceptually. You have two Singles, each of them will emit a single next event. And you want to output a single next event. In order to do this, you will need to wait for both sources to emit their next events and then combine them into a single event. This is exactly what zip
does. You likely want this:
Single.zip(single1, single2) { $0 + $1 }
or
Single.zip(single1, single2)
.map { $0.0 + $0.1 }
Ok, thanks for the reply and code snippet. I'm new to Swift, RxSwift and iOS, so not everything is obvious and clear for me in this area. I'll check this approach later.
Okay. And by all means, join the RxSwift slack channel (https://rxslack.herokuapp.com) and feel free to ask questions on Stack Overflow.
I used solution proposed by @danielt1263 and it seems it solved my problem. I rewrote my Single
s with arrays into Observable
s with arrays, zipped them with Observable.zip
operator, added both arrays and then processed them. In the end I produced Observable
with the new object instead of Single
.
That is not the solution I proposed, but I'm glad it worked for you. I said to use Single.zip(single1, single2)
not Observable.zip(single1.asObservable(), single2.assObservable())
... The result is the same, just the types are different.
Yes, I applied the same idea, but on the different data types. Actually I omitted asObservable()
invocations on Single
s because I rewrote Single
s into Observable
s.
It looks like that:
Observable.zip(observableOne, observableTwo) { $0 + $1 }