Skip to content

Commit 3075c3f

Browse files
authored
Merge pull request #193 from RxSwiftCommunity/operator/partition
Partition operator
2 parents 30ce4b5 + e5e2e22 commit 3075c3f

File tree

9 files changed

+307
-1
lines changed

9 files changed

+307
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Changelog
44
master
55
-----
66

7-
Nothing yet!
7+
- added `partition(_:)` operator
88

99
3.4.0
1010
-----

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
- [ofType()](ofType) operator, filters the elements of an observable sequence, if that is an instance of the supplied type.
4545
- [toSortedArray()](mapMany) operator, converts an Observable into another Observable that emits the whole sequence as a single array sorted using the provided closure and then terminates.
4646
- [count(predicate)](count) operator, counts the number of items emitted by an Observable. If predicate exists, then counts the number of items satisfying it.
47+
- [partition](partition) operator, partition a stream into two separate streams of elements that match, and don't match, the provided predicate.
4748
- **UIViewPropertyAnimator** [animate()](UIViewPropertyAnimator.animate) operator, returns a Completable that completes as soon as the animation ends.
4849
- **UIViewPropertyAnimator** [fractionComplete](UIViewPropertyAnimator.fractionComplete) binder, provides a reactive way to bind to `UIViewPropertyAnimator.fractionComplete`.
4950
*/
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
example("partition") {
16+
let numbers = Observable
17+
.of(1, 2, 3, 5, 8, 13, 18, 21, 23)
18+
19+
let (evens, odds) = numbers.partition { $0 % 2 == 0 }
20+
21+
_ = evens.debug("even").subscribe()
22+
_ = odds.debug("odds").subscribe()
23+
}
24+
//: [Next](@next)
25+

Readme.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ These operators are much like the RxSwift & RxCocoa core operators, but provide
8181
* [Observable.zip(with:)](#zipwith)
8282
* [withUnretained](#withunretained)
8383
* [count](#count)
84+
* [partition](#partition)
8485

8586
There are two more available operators for `materialize()`'d sequences:
8687

@@ -598,6 +599,20 @@ next(3)
598599
completed
599600
```
600601

602+
#### partition
603+
604+
Partition a stream into two separate streams of elements that match, and don't match, the provided predicate.
605+
606+
```swift
607+
let numbers = Observable
608+
.of(1, 2, 3, 4, 5, 6)
609+
610+
let (evens, odds) = numbers.partition { $0 % 2 == 0 }
611+
612+
_ = evens.debug("even").subscribe() // emits 2, 4, 6
613+
_ = odds.debug("odds").subscribe() // emits 1, 3, 5
614+
```
615+
601616
Reactive Extensions details
602617
===========
603618

RxSwiftExt.xcodeproj/project.pbxproj

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,18 @@
150150
98309EAF1EDF14AC00BD07D9 /* flatMapSync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98309EAE1EDF14AC00BD07D9 /* flatMapSync.swift */; };
151151
98309EB11EDF159500BD07D9 /* filterMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98309EB01EDF159500BD07D9 /* filterMap.swift */; };
152152
98309EB41EDF167300BD07D9 /* FilterMapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98309EB21EDF161700BD07D9 /* FilterMapTests.swift */; };
153+
A23E148721A9EFC000CD5B2F /* partition.swift in Sources */ = {isa = PBXBuildFile; fileRef = A23E148621A9EFC000CD5B2F /* partition.swift */; };
154+
A23E148821A9EFC000CD5B2F /* partition.swift in Sources */ = {isa = PBXBuildFile; fileRef = A23E148621A9EFC000CD5B2F /* partition.swift */; };
155+
A23E148921A9EFC000CD5B2F /* partition.swift in Sources */ = {isa = PBXBuildFile; fileRef = A23E148621A9EFC000CD5B2F /* partition.swift */; };
156+
A23E148B21A9F03600CD5B2F /* partition+RxCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = A23E148A21A9F03600CD5B2F /* partition+RxCocoa.swift */; };
157+
A23E148C21A9F03600CD5B2F /* partition+RxCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = A23E148A21A9F03600CD5B2F /* partition+RxCocoa.swift */; };
158+
A23E148D21A9F03600CD5B2F /* partition+RxCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = A23E148A21A9F03600CD5B2F /* partition+RxCocoa.swift */; };
159+
A23E148F21A9F10D00CD5B2F /* PartitionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A23E148E21A9F10D00CD5B2F /* PartitionTests.swift */; };
160+
A23E149021A9F10D00CD5B2F /* PartitionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A23E148E21A9F10D00CD5B2F /* PartitionTests.swift */; };
161+
A23E149121A9F10D00CD5B2F /* PartitionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A23E148E21A9F10D00CD5B2F /* PartitionTests.swift */; };
162+
A23E149321A9F73500CD5B2F /* PartitionTests+RxCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = A23E149221A9F73500CD5B2F /* PartitionTests+RxCocoa.swift */; };
163+
A23E149421A9F73500CD5B2F /* PartitionTests+RxCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = A23E149221A9F73500CD5B2F /* PartitionTests+RxCocoa.swift */; };
164+
A23E149521A9F73500CD5B2F /* PartitionTests+RxCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = A23E149221A9F73500CD5B2F /* PartitionTests+RxCocoa.swift */; };
153165
B69B45492190C27D00F30418 /* count.swift in Sources */ = {isa = PBXBuildFile; fileRef = B69B45482190C27D00F30418 /* count.swift */; };
154166
B69B454A2190C3AE00F30418 /* count.swift in Sources */ = {isa = PBXBuildFile; fileRef = B69B45482190C27D00F30418 /* count.swift */; };
155167
B69B454B2190C3AF00F30418 /* count.swift in Sources */ = {isa = PBXBuildFile; fileRef = B69B45482190C27D00F30418 /* count.swift */; };
@@ -336,6 +348,10 @@
336348
98309EB01EDF159500BD07D9 /* filterMap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = filterMap.swift; path = Source/RxSwift/filterMap.swift; sourceTree = SOURCE_ROOT; };
337349
98309EB21EDF161700BD07D9 /* FilterMapTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FilterMapTests.swift; sourceTree = "<group>"; };
338350
9DAB77851D67639C007E85BC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Source/Info.plist; sourceTree = SOURCE_ROOT; };
351+
A23E148621A9EFC000CD5B2F /* partition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = partition.swift; sourceTree = "<group>"; };
352+
A23E148A21A9F03600CD5B2F /* partition+RxCocoa.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "partition+RxCocoa.swift"; sourceTree = "<group>"; };
353+
A23E148E21A9F10D00CD5B2F /* PartitionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartitionTests.swift; sourceTree = "<group>"; };
354+
A23E149221A9F73500CD5B2F /* PartitionTests+RxCocoa.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PartitionTests+RxCocoa.swift"; sourceTree = "<group>"; };
339355
B69B45482190C27D00F30418 /* count.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = count.swift; sourceTree = "<group>"; };
340356
B69B454C2190C3BC00F30418 /* CountTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountTests.swift; sourceTree = "<group>"; };
341357
BF515CDF1F3F370600492640 /* curry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = curry.swift; path = Source/Tools/curry.swift; sourceTree = SOURCE_ROOT; };
@@ -466,6 +482,7 @@
466482
538607EF1E6F589E000361DE /* not+RxCocoa.swift */,
467483
4A73956B206D501300E2BE2D /* UIViewPropertyAnimator+Rx.swift */,
468484
1958B5F0216768D900CAF1D3 /* unwrap+SharedSequence.swift */,
485+
A23E148A21A9F03600CD5B2F /* partition+RxCocoa.swift */,
469486
);
470487
path = RxCocoa;
471488
sourceTree = "<group>";
@@ -478,6 +495,7 @@
478495
53C79D5F1E6F5AAB00CD9B6A /* NotTests+RxCocoa.swift */,
479496
1A8741AB20745A91004BB762 /* UIViewPropertyAnimatorTests+Rx.swift */,
480497
1958B5F521676ECB00CAF1D3 /* unrwapTests+SharedSequence.swift */,
498+
A23E149221A9F73500CD5B2F /* PartitionTests+RxCocoa.swift */,
481499
);
482500
name = RxCocoa;
483501
path = Tests/RxCocoa;
@@ -515,6 +533,7 @@
515533
C4D2153E20118A81009804AE /* ofType.swift */,
516534
BF79DA09206C145D008AA708 /* withUnretained.swift */,
517535
DC612871209E80810053CBB7 /* mapMany.swift */,
536+
A23E148621A9EFC000CD5B2F /* partition.swift */,
518537
);
519538
path = RxSwift;
520539
sourceTree = "<group>";
@@ -551,6 +570,7 @@
551570
538607CB1E6F367A000361DE /* WeakTests.swift */,
552571
58C54E184069446EDEE748F9 /* ZipWithTest.swift */,
553572
BF79DA0D206C185B008AA708 /* WithUnretainedTests.swift */,
573+
A23E148E21A9F10D00CD5B2F /* PartitionTests.swift */,
554574
);
555575
name = RxSwift;
556576
path = Tests/RxSwift;
@@ -995,6 +1015,7 @@
9951015
780CB21520A0ED1C00FD3F39 /* toSortedArray.swift in Sources */,
9961016
98309EB11EDF159500BD07D9 /* filterMap.swift in Sources */,
9971017
53F336E81E70CBF700D35D38 /* distinct+RxCocoa.swift in Sources */,
1018+
A23E148721A9EFC000CD5B2F /* partition.swift in Sources */,
9981019
538607AE1E6F334B000361DE /* ignore.swift in Sources */,
9991020
BF79DA0A206C145D008AA708 /* withUnretained.swift in Sources */,
10001021
BF515CE01F3F370600492640 /* curry.swift in Sources */,
@@ -1003,6 +1024,7 @@
10031024
BF515CE21F3F371600492640 /* fromAsync.swift in Sources */,
10041025
DC612872209E80810053CBB7 /* mapMany.swift in Sources */,
10051026
5386076F1E6F1C0A000361DE /* mapTo+RxCocoa.swift in Sources */,
1027+
A23E148B21A9F03600CD5B2F /* partition+RxCocoa.swift in Sources */,
10061028
538607B71E6F334B000361DE /* repeatWithBehavior.swift in Sources */,
10071029
538607AC1E6F334B000361DE /* catchErrorJustComplete.swift in Sources */,
10081030
58C5450451345D65DF48F6C5 /* zipWith.swift in Sources */,
@@ -1014,6 +1036,7 @@
10141036
buildActionMask = 2147483647;
10151037
files = (
10161038
538607E01E6F36A9000361DE /* CascadeTests.swift in Sources */,
1039+
A23E148F21A9F10D00CD5B2F /* PartitionTests.swift in Sources */,
10171040
53F336EA1E70D59000D35D38 /* DistinctTests+RxCocoa.swift in Sources */,
10181041
98309EB41EDF167300BD07D9 /* FilterMapTests.swift in Sources */,
10191042
B69B454E2190C3CC00F30418 /* CountTests.swift in Sources */,
@@ -1026,6 +1049,7 @@
10261049
538607E21E6F36A9000361DE /* DistinctTests.swift in Sources */,
10271050
538607EA1E6F36A9000361DE /* PausableTests.swift in Sources */,
10281051
538607E91E6F36A9000361DE /* OnceTests.swift in Sources */,
1052+
A23E149321A9F73500CD5B2F /* PartitionTests+RxCocoa.swift in Sources */,
10291053
BF79DA0E206C185B008AA708 /* WithUnretainedTests.swift in Sources */,
10301054
538607EE1E6F36A9000361DE /* WeakTests.swift in Sources */,
10311055
780CB21920A0ED3B00FD3F39 /* ToSortedArrayTests.swift in Sources */,
@@ -1080,6 +1104,8 @@
10801104
62512C791F0EAF950083A89F /* retryWithBehavior.swift in Sources */,
10811105
DC612873209E80810053CBB7 /* mapMany.swift in Sources */,
10821106
BF79DA0B206C145D008AA708 /* withUnretained.swift in Sources */,
1107+
A23E148821A9EFC000CD5B2F /* partition.swift in Sources */,
1108+
A23E148C21A9F03600CD5B2F /* partition+RxCocoa.swift in Sources */,
10831109
BF515CE81F3F3B0000492640 /* fromAsync.swift in Sources */,
10841110
BF515CEA1F3F3B0300492640 /* curry.swift in Sources */,
10851111
62512C691F0EAF850083A89F /* not+RxCocoa.swift in Sources */,
@@ -1100,6 +1126,7 @@
11001126
62512C901F0EB17D0083A89F /* MapToTests+RxCocoa.swift in Sources */,
11011127
62512C961F0EB1850083A89F /* IgnoreErrorsTests.swift in Sources */,
11021128
BF515CE61F3F3AF500492640 /* FromAsyncTests.swift in Sources */,
1129+
A23E149421A9F73500CD5B2F /* PartitionTests+RxCocoa.swift in Sources */,
11031130
62512CA41F0EB1850083A89F /* FilterMapTests.swift in Sources */,
11041131
B69B454F2190C3CC00F30418 /* CountTests.swift in Sources */,
11051132
D7C72A3F1FDC5C5D00EAAAAB /* NwiseTests.swift in Sources */,
@@ -1114,6 +1141,7 @@
11141141
62512C9E1F0EB1850083A89F /* PausableBufferedTests.swift in Sources */,
11151142
62512CA01F0EB1850083A89F /* RetryWithBehaviorTests.swift in Sources */,
11161143
C4D2154320118FB9009804AE /* Observable+OfTypeTests.swift in Sources */,
1144+
A23E149021A9F10D00CD5B2F /* PartitionTests.swift in Sources */,
11171145
780CB21E20A0EE8300FD3F39 /* MapManyTests.swift in Sources */,
11181146
62512C911F0EB17F0083A89F /* NotTests+RxCocoa.swift in Sources */,
11191147
62512C9A1F0EB1850083A89F /* Materialized+elementsTests.swift in Sources */,
@@ -1159,6 +1187,8 @@
11591187
E39C41EA1F18B08A007F2ACD /* retryWithBehavior.swift in Sources */,
11601188
DC612874209E80810053CBB7 /* mapMany.swift in Sources */,
11611189
BF79DA0C206C145D008AA708 /* withUnretained.swift in Sources */,
1190+
A23E148921A9EFC000CD5B2F /* partition.swift in Sources */,
1191+
A23E148D21A9F03600CD5B2F /* partition+RxCocoa.swift in Sources */,
11621192
BF515CE91F3F3B0100492640 /* fromAsync.swift in Sources */,
11631193
BF515CEB1F3F3B0300492640 /* curry.swift in Sources */,
11641194
E39C41DA1F18B086007F2ACD /* not+RxCocoa.swift in Sources */,
@@ -1179,6 +1209,7 @@
11791209
E39C420F1F18B13E007F2ACD /* UnwrapTests.swift in Sources */,
11801210
E39C42121F18B13E007F2ACD /* FilterMapTests.swift in Sources */,
11811211
BF515CE71F3F3AF500492640 /* FromAsyncTests.swift in Sources */,
1212+
A23E149521A9F73500CD5B2F /* PartitionTests+RxCocoa.swift in Sources */,
11821213
E39C41FF1F18B13A007F2ACD /* NotTests+RxCocoa.swift in Sources */,
11831214
B69B45502190C3CD00F30418 /* CountTests.swift in Sources */,
11841215
D7C72A401FDC5C5D00EAAAAB /* NwiseTests.swift in Sources */,
@@ -1193,6 +1224,7 @@
11931224
E39C420E1F18B13E007F2ACD /* RetryWithBehaviorTests.swift in Sources */,
11941225
E39C42061F18B13E007F2ACD /* IgnoreWhenTests.swift in Sources */,
11951226
C4D2154420118FBA009804AE /* Observable+OfTypeTests.swift in Sources */,
1227+
A23E149121A9F10D00CD5B2F /* PartitionTests.swift in Sources */,
11961228
780CB21F20A0EE8300FD3F39 /* MapManyTests.swift in Sources */,
11971229
E39C420C1F18B13E007F2ACD /* PausableBufferedTests.swift in Sources */,
11981230
E39C42041F18B13E007F2ACD /* IgnoreErrorsTests.swift in Sources */,
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// partition+RxCocoa.swift
3+
// RxSwiftExt
4+
//
5+
// Created by Shai Mishali on 24/11/2018.
6+
// Copyright © 2018 RxSwiftCommunity. All rights reserved.
7+
//
8+
9+
import RxSwift
10+
import RxCocoa
11+
12+
public extension SharedSequence {
13+
/**
14+
Partition a stream into two separate streams of elements that match, and don't match, the provided predicate.
15+
16+
- parameter predicate: A predicate used to filter matching and non-matching elements.
17+
18+
- returns: A tuple of two streams of elements that match, and don't match, the provided predicate.
19+
*/
20+
func partition(_ predicate: @escaping (E) -> Bool) -> (matches: SharedSequence<S, E>,
21+
nonMatches: SharedSequence<S, E>) {
22+
let hits = self.filter(predicate)
23+
let misses = self.filter { !predicate($0) }
24+
25+
return (hits, misses)
26+
}
27+
}

Source/RxSwift/partition.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// partition.swift
3+
// RxSwiftExt
4+
//
5+
// Created by Shai Mishali on 24/11/2018.
6+
// Copyright © 2018 RxSwiftCommunity. All rights reserved.
7+
//
8+
9+
import RxSwift
10+
11+
public extension ObservableType {
12+
/**
13+
Partition a stream into two separate streams of elements that match, and don't match, the provided predicate.
14+
15+
- parameter predicate: A predicate used to filter matching and non-matching elements.
16+
17+
- returns: A tuple of two streams of elements that match, and don't match, the provided predicate.
18+
*/
19+
func partition(_ predicate: @escaping (E) throws -> Bool) -> (matches: Observable<E>, nonMatches: Observable<E>) {
20+
let stream = self.share()
21+
let hits = stream.filter(predicate)
22+
let misses = stream.filter {
23+
return !(try predicate($0))
24+
}
25+
return (hits, misses)
26+
}
27+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
//
2+
// PartitionTests+RxCocoa.swift
3+
// RxSwiftExt
4+
//
5+
// Created by Shai Mishali on 24/11/2018.
6+
// Copyright © 2018 RxSwiftCommunity. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import RxSwift
11+
import RxCocoa
12+
import RxTest
13+
import XCTest
14+
import RxSwiftExt
15+
16+
class PartitionSharedStrategyTests: XCTestCase {
17+
private var scheduler = TestScheduler(initialClock: 0)
18+
private var stream: Driver<Int>!
19+
20+
override func setUp() {
21+
super.setUp()
22+
scheduler = TestScheduler(initialClock: 0)
23+
let events = (0...10).map { i -> Recorded<Event<Int>> in
24+
return .next(i * 10, i)
25+
} + [.completed(100)]
26+
27+
stream = scheduler
28+
.createHotObservable(events)
29+
.asDriver(onErrorDriveWith: .never())
30+
}
31+
32+
func testPartitionBothMatch() {
33+
let (evens, odds) = stream.partition { $0 % 2 == 0 }
34+
35+
let evensObserver = scheduler.createObserver(Int.self)
36+
_ = evens.drive(evensObserver)
37+
38+
let oddsObserver = scheduler.createObserver(Int.self)
39+
_ = odds.drive(oddsObserver)
40+
41+
scheduler.start()
42+
43+
XCTAssertEqual(oddsObserver.events, Recorded.events([
44+
.next(10, 1),
45+
.next(30, 3),
46+
.next(50, 5),
47+
.next(70, 7),
48+
.next(90, 9),
49+
.completed(100)
50+
]))
51+
52+
XCTAssertEqual(evensObserver.events, Recorded.events([
53+
.next(0, 0),
54+
.next(20, 2),
55+
.next(40, 4),
56+
.next(60, 6),
57+
.next(80, 8),
58+
.next(100, 10),
59+
.completed(100)
60+
]))
61+
}
62+
63+
func testPartitionOneSideMatch() {
64+
let (all, none) = stream.partition { $0 <= 10 }
65+
66+
let allObserver = scheduler.createObserver(Int.self)
67+
_ = all.drive(allObserver)
68+
69+
let noneObserver = scheduler.createObserver(Int.self)
70+
_ = none.drive(noneObserver)
71+
72+
scheduler.start()
73+
74+
XCTAssertEqual(allObserver.events, Recorded.events([
75+
.next(0, 0),
76+
.next(10, 1),
77+
.next(20, 2),
78+
.next(30, 3),
79+
.next(40, 4),
80+
.next(50, 5),
81+
.next(60, 6),
82+
.next(70, 7),
83+
.next(80, 8),
84+
.next(90, 9),
85+
.next(100, 10),
86+
.completed(100)
87+
]))
88+
89+
XCTAssertEqual(noneObserver.events, [.completed(100)])
90+
}
91+
}

0 commit comments

Comments
 (0)