ReactiveX / RxSwift

Reactive Programming in Swift

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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:

public func toArray()
-> Single<[Element]> {
PrimitiveSequence(raw: ToArray(source: self.asObservable()))
}

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 Elements.

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.

The second one here is what merge() actually is (and also why it's Observable and not Single at the output):
image

The first variation there is actually a regular flatMap - you switch to the second Single, discarding the first one.

I used solution proposed by @danielt1263 and it seems it solved my problem. I rewrote my Singles with arrays into Observables 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 Singles because I rewrote Singles into Observables.

It looks like that:

Observable.zip(observableOne, observableTwo) { $0 + $1 }