Skip to content

Commit af6764d

Browse files
jdishofreak4pc
authored andcommitted
merge(with:) operator (#156)
1 parent 3ac5d25 commit af6764d

File tree

9 files changed

+178
-2
lines changed

9 files changed

+178
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Changelog
33

44
- added `reachedBottom(offset:)` for `UIScrollView`
55
- `once` now uses a `NSRecursiveLock` instead of the deprecated `OSAtomicOr32OrigBarrier`
6+
- added `merge(with:)` for `Observable`
67

78
5.0.0
89
-----

Playground/RxSwiftExtPlayground.playground/Pages/Index.xcplaygroundpage/Contents.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
- **UIViewPropertyAnimator** [animate()](UIViewPropertyAnimator.animate) operator, returns a Completable that completes as soon as the animation ends.
5050
- **UIViewPropertyAnimator** [fractionComplete](UIViewPropertyAnimator.fractionComplete) binder, provides a reactive way to bind to `UIViewPropertyAnimator.fractionComplete`.
5151
- **UIScrollView** [reachedBottom](UIScrollView.reachedBottom) emits events when UIScrollView is scrolled to the bottom.
52+
- [Observable.merge(with:)](mergeWith) merges elements from observable sequences into a single observable sequence.
5253
*/
5354

5455
//: [Next >>](@next)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*:
2+
> # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3+
4+
1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5+
1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6+
1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7+
1. Choose `View > Show Debug Area`
8+
*/
9+
10+
//: [Previous](@previous)
11+
12+
import RxSwift
13+
import RxSwiftExt
14+
15+
/*:
16+
## merge(with:)
17+
Merges elements from observable sequences into a single observable sequence.
18+
19+
*/
20+
example("merge(with:)") {
21+
let oddNumbers = [1, 3, 5, 7, 9]
22+
let evenNumbers = [2, 4, 6, 8, 10]
23+
let otherNumbers = [1, 5 ,6, 2]
24+
25+
let disposeBag = DisposeBag()
26+
let oddStream = Observable.from(oddNumbers)
27+
let evenStream = Observable.from(evenNumbers)
28+
let otherStream = Observable.from(otherNumbers)
29+
30+
oddStream.merge(with: evenStream, otherStream)
31+
.subscribe(onNext: { result in
32+
print(result)
33+
})
34+
.disposed(by: disposeBag)
35+
}
36+
37+
//: [Next](@next)

Playground/RxSwiftExtPlayground.playground/Pages/zipWith.xcplaygroundpage/Contents.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import RxSwiftExt
1818
of the observable sequences have produced an element at a corresponding index.
1919

2020
*/
21-
example("zip values") {
21+
example("zip(with:)") {
2222
let numbers = [1,2,3]
2323
let strings = ["a", "b", "c"]
2424

Playground/RxSwiftExtPlayground.playground/contents.xcplayground

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<page name='and'/>
2424
<page name='nwise'/>
2525
<page name='zipWith'/>
26+
<page name='mergeWith'/>
2627
<page name='ofType'/>
2728
<page name='mapMany'/>
2829
<page name='toSortedArray'/>

Readme.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ These operators are much like the RxSwift & RxCocoa core operators, but provide
7272
* [filterMap](#filtermap)
7373
* [Observable.fromAsync](#fromasync)
7474
* [Observable.zip(with:)](#zipwith)
75+
* [Observable.merge(with:)](#mergewith)
7576
* [withUnretained](#withunretained)
7677
* [count](#count)
7778
* [partition](#partition)
@@ -502,7 +503,7 @@ observableService("Foo", 0)
502503
.disposed(by: disposeBag)
503504
```
504505

505-
#### zipWith
506+
#### zip(with:)
506507

507508
Convenience version of `Observable.zip(_:)`. Merges the specified observable sequences into one observable sequence by using the selector function whenever all
508509
of the observable sequences have produced an element at a corresponding index.
@@ -524,6 +525,25 @@ next("b2")
524525
next("c3")
525526
```
526527

528+
#### merge(with:)
529+
530+
Convenience version of `Observable.merge(_:)`. Merges elements from the observable sequence with those of a different observable sequences into a single observable sequence.
531+
532+
```swift
533+
let oddStream = Observable.of(1, 3, 5)
534+
let evenStream = Observable.of(2, 4, 6)
535+
let otherStream = Observable.of(1, 5, 6)
536+
537+
oddStream.merge(with: evenStream, otherStream)
538+
.subscribe(onNext: { result in
539+
print(result)
540+
})
541+
```
542+
543+
```
544+
1 2 1 3 4 5 5 6 6
545+
```
546+
527547
#### ofType
528548

529549
The ofType operator filters the elements of an observable sequence, if that is an instance of the supplied type.

RxSwiftExt.xcodeproj/project.pbxproj

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,12 @@
143143
780CB21F20A0EE8300FD3F39 /* MapManyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 780CB21C20A0EE8300FD3F39 /* MapManyTests.swift */; };
144144
78199613228449CA00340AF4 /* UIScrollView+reachedBottom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78199612228449CA00340AF4 /* UIScrollView+reachedBottom.swift */; };
145145
7819961622844EF800340AF4 /* UIScrollView+reachedBottomTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7819961422844E9D00340AF4 /* UIScrollView+reachedBottomTests.swift */; };
146+
782485932298A708005CF8CC /* mergeWith.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782485922298A702005CF8CC /* mergeWith.swift */; };
147+
782485952298A785005CF8CC /* MergeWithTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782485942298A785005CF8CC /* MergeWithTests.swift */; };
148+
782485962298A785005CF8CC /* MergeWithTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782485942298A785005CF8CC /* MergeWithTests.swift */; };
149+
782485972298A785005CF8CC /* MergeWithTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782485942298A785005CF8CC /* MergeWithTests.swift */; };
150+
7824859B2298ADE2005CF8CC /* mergeWith.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782485922298A702005CF8CC /* mergeWith.swift */; };
151+
7824859C2298ADE2005CF8CC /* mergeWith.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782485922298A702005CF8CC /* mergeWith.swift */; };
146152
789682E720408A7500545396 /* mapAt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CF5F8B2202D6C5F00C1BA97 /* mapAt.swift */; };
147153
789682E820408A7700545396 /* mapAt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CF5F8B2202D6C5F00C1BA97 /* mapAt.swift */; };
148154
8CF5F8AF202D62AB00C1BA97 /* MapAtTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CF5F8AD202D622000C1BA97 /* MapAtTests.swift */; };
@@ -353,6 +359,8 @@
353359
780CB21C20A0EE8300FD3F39 /* MapManyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapManyTests.swift; sourceTree = "<group>"; };
354360
78199612228449CA00340AF4 /* UIScrollView+reachedBottom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIScrollView+reachedBottom.swift"; sourceTree = "<group>"; };
355361
7819961422844E9D00340AF4 /* UIScrollView+reachedBottomTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIScrollView+reachedBottomTests.swift"; sourceTree = "<group>"; };
362+
782485922298A702005CF8CC /* mergeWith.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = mergeWith.swift; sourceTree = "<group>"; };
363+
782485942298A785005CF8CC /* MergeWithTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MergeWithTests.swift; sourceTree = "<group>"; };
356364
8CF5F8AD202D622000C1BA97 /* MapAtTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapAtTests.swift; sourceTree = "<group>"; };
357365
8CF5F8B2202D6C5F00C1BA97 /* mapAt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = mapAt.swift; sourceTree = "<group>"; };
358366
98309EAE1EDF14AC00BD07D9 /* flatMapSync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = flatMapSync.swift; path = Source/RxSwift/flatMapSync.swift; sourceTree = SOURCE_ROOT; };
@@ -537,6 +545,7 @@
537545
538607A11E6F334B000361DE /* mapTo.swift */,
538546
8CF5F8B2202D6C5F00C1BA97 /* mapAt.swift */,
539547
66C663051EA0ECD9005245C4 /* materialized+elements.swift */,
548+
782485922298A702005CF8CC /* mergeWith.swift */,
540549
538607A31E6F334B000361DE /* not.swift */,
541550
D7C72A411FDC5D8F00EAAAAB /* nwise.swift */,
542551
538607A41E6F334B000361DE /* ObservableType+Weak.swift */,
@@ -576,6 +585,7 @@
576585
8CF5F8AD202D622000C1BA97 /* MapAtTests.swift */,
577586
780CB21C20A0EE8300FD3F39 /* MapManyTests.swift */,
578587
6662395D1E9E0950009BB134 /* Materialized+elementsTests.swift */,
588+
782485942298A785005CF8CC /* MergeWithTests.swift */,
579589
538607C41E6F367A000361DE /* NotTests.swift */,
580590
D7C72A3D1FDC5C5D00EAAAAB /* NwiseTests.swift */,
581591
538607C51E6F367A000361DE /* OnceTests.swift */,
@@ -1020,6 +1030,7 @@
10201030
538607AB1E6F334B000361DE /* cascade.swift in Sources */,
10211031
538607F01E6F589E000361DE /* not+RxCocoa.swift in Sources */,
10221032
538607B81E6F334B000361DE /* retryWithBehavior.swift in Sources */,
1033+
782485932298A708005CF8CC /* mergeWith.swift in Sources */,
10231034
66C663061EA0ECD9005245C4 /* materialized+elements.swift in Sources */,
10241035
538607B51E6F334B000361DE /* once.swift in Sources */,
10251036
4A73956C206D501300E2BE2D /* UIViewPropertyAnimator+Rx.swift in Sources */,
@@ -1070,6 +1081,7 @@
10701081
BF515CE51F3F3AF400492640 /* FromAsyncTests.swift in Sources */,
10711082
538607EB1E6F36A9000361DE /* RetryWithBehaviorTests.swift in Sources */,
10721083
D7C72A3E1FDC5C5D00EAAAAB /* NwiseTests.swift in Sources */,
1084+
782485952298A785005CF8CC /* MergeWithTests.swift in Sources */,
10731085
8CF5F8AF202D62AB00C1BA97 /* MapAtTests.swift in Sources */,
10741086
538607E21E6F36A9000361DE /* DistinctTests.swift in Sources */,
10751087
538607EA1E6F36A9000361DE /* PausableTests.swift in Sources */,
@@ -1107,6 +1119,7 @@
11071119
files = (
11081120
62512C7A1F0EAF950083A89F /* unwrap.swift in Sources */,
11091121
780CB21620A0ED1C00FD3F39 /* toSortedArray.swift in Sources */,
1122+
7824859B2298ADE2005CF8CC /* mergeWith.swift in Sources */,
11101123
62512C741F0EAF950083A89F /* ObservableType+Weak.swift in Sources */,
11111124
B69B454A2190C3AE00F30418 /* count.swift in Sources */,
11121125
62512C6F1F0EAF950083A89F /* ignoreErrors.swift in Sources */,
@@ -1170,6 +1183,7 @@
11701183
62512CA01F0EB1850083A89F /* RetryWithBehaviorTests.swift in Sources */,
11711184
C4D2154320118FB9009804AE /* Observable+OfTypeTests.swift in Sources */,
11721185
A23E149021A9F10D00CD5B2F /* PartitionTests.swift in Sources */,
1186+
782485962298A785005CF8CC /* MergeWithTests.swift in Sources */,
11731187
780CB21E20A0EE8300FD3F39 /* MapManyTests.swift in Sources */,
11741188
62512C911F0EB17F0083A89F /* NotTests+RxCocoa.swift in Sources */,
11751189
62512C9A1F0EB1850083A89F /* Materialized+elementsTests.swift in Sources */,
@@ -1192,6 +1206,7 @@
11921206
files = (
11931207
E39C41EB1F18B08A007F2ACD /* unwrap.swift in Sources */,
11941208
780CB21720A0ED1C00FD3F39 /* toSortedArray.swift in Sources */,
1209+
7824859C2298ADE2005CF8CC /* mergeWith.swift in Sources */,
11951210
E39C41E51F18B08A007F2ACD /* ObservableType+Weak.swift in Sources */,
11961211
B69B454B2190C3AF00F30418 /* count.swift in Sources */,
11971212
E39C41E01F18B08A007F2ACD /* ignoreErrors.swift in Sources */,
@@ -1255,6 +1270,7 @@
12551270
E39C42061F18B13E007F2ACD /* IgnoreWhenTests.swift in Sources */,
12561271
C4D2154420118FBA009804AE /* Observable+OfTypeTests.swift in Sources */,
12571272
A23E149121A9F10D00CD5B2F /* PartitionTests.swift in Sources */,
1273+
782485972298A785005CF8CC /* MergeWithTests.swift in Sources */,
12581274
780CB21F20A0EE8300FD3F39 /* MapManyTests.swift in Sources */,
12591275
E39C420C1F18B13E007F2ACD /* PausableBufferedTests.swift in Sources */,
12601276
E39C42041F18B13E007F2ACD /* IgnoreErrorsTests.swift in Sources */,

Source/RxSwift/mergeWith.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//
2+
// mergeWith.swift
3+
// RxSwiftExt
4+
//
5+
// Created by Joan Disho on 12/05/18.
6+
// Copyright © 2018 RxSwift Community. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import RxSwift
11+
12+
extension Observable {
13+
/**
14+
Merges elements from the observable sequence with those of a different observable sequences into a single observable sequence.
15+
16+
- parameter with: Other observables.
17+
- returns: The observable sequence that merges the elements of the observable sequences.
18+
*/
19+
public func merge(with others: Observable<Element>...) -> Observable<Element> {
20+
return Observable.merge([self] + others)
21+
}
22+
}

Tests/RxSwift/MergeWithTests.swift

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//
2+
// MergeWithTests.swift
3+
// RxSwiftExt
4+
//
5+
// Created by Joan Disho on 28/05/18.
6+
// Copyright © 2018 RxSwift Community. All rights reserved.
7+
//
8+
9+
import XCTest
10+
import RxSwift
11+
import RxTest
12+
13+
enum MergeWithError: Error {
14+
case error
15+
}
16+
17+
class MergeWithTests: XCTestCase {
18+
fileprivate func runAndObserve<T>(_ sequence: Observable<T>) -> TestableObserver<T> {
19+
let scheduler = TestScheduler(initialClock: 0)
20+
let observer = scheduler.createObserver(T.self)
21+
_ = sequence.asObservable().subscribe(observer)
22+
scheduler.start()
23+
return observer
24+
}
25+
26+
func testMergeWith_Numbers() {
27+
let oddNumbers = [1, 3, 5, 7, 9]
28+
let evenNumbers = [2, 4, 6, 8, 10]
29+
30+
let oddStream = Observable.from(oddNumbers)
31+
let evenStream = Observable.from(evenNumbers)
32+
33+
let observer1 = runAndObserve(oddStream.merge(with: evenStream))
34+
let observer2 = runAndObserve(evenStream.merge(with: oddStream))
35+
36+
let correct1 = Recorded.events([
37+
.next(0, 1),
38+
.next(0, 2),
39+
.next(0, 3),
40+
.next(0, 4),
41+
.next(0, 5),
42+
.next(0, 6),
43+
.next(0, 7),
44+
.next(0, 8),
45+
.next(0, 9),
46+
.next(0, 10),
47+
.completed(0)
48+
])
49+
50+
let correct2 = Recorded.events([
51+
.next(0, 2),
52+
.next(0, 1),
53+
.next(0, 4),
54+
.next(0, 3),
55+
.next(0, 6),
56+
.next(0, 5),
57+
.next(0, 8),
58+
.next(0, 7),
59+
.next(0, 10),
60+
.next(0, 9),
61+
.completed(0)
62+
])
63+
64+
XCTAssertEqual(observer1.events, correct1)
65+
XCTAssertEqual(observer2.events, correct2)
66+
}
67+
68+
func testMergeWith_Error() {
69+
let numberStream = Observable.from([1, 2, 3, 4])
70+
let errorStream = Observable<Int>.error(MergeWithError.error)
71+
72+
let observer = runAndObserve(numberStream.merge(with: errorStream))
73+
74+
let expected = Recorded<Event<Int>>.events([.error(0, MergeWithError.error)])
75+
76+
XCTAssertEqual(observer.events, expected)
77+
}
78+
}

0 commit comments

Comments
 (0)