Contents
Chào bạn đến với Fx Studio. Chúng ta đã tìm hiểu về Operators là gì rồi. Bài viết này sẽ đưa bạn tới nhóm toán tử đầu tiên. Đó là Filtering Operators.
Nhưng trước tiên, bạn cần phải biết được Operators là gì. Và nếu như bạn chưa biết về nó thì có thể tham khảo link sau:
Và khi mọi việc đã ổn rồi, thì …
Bắt đầu thôi!
Chuẩn bị
-
- Xcode 11
- Swift 5
- Playground
Vẫn là em Playground huyền thoại. Chúng ta vẫn còn dùng tới nó để demo code cho bài viết này. Bạn chỉ cần tạo mới 1 file Playground từ project mà đã cài đặt ngay từ bài đầu của series. Bạn có thể checkout code lại đây.
Filtering Operators
Bạn đã đi qua các phần cơ bản với RxSwift, nếu như bạn tìm hiểu kĩ và tiến từng bước chắc chắn, thì tới đây bạn sẽ bắt đầu tăng tốc được rồi. Và luyện tập nhiều hơn để quen với thế giới Rx.
Với nhóm Filtering Operators này giúp cho bạn sẽ có được những giá trị mà bạn mong muốn nhận được. Và bạn đã dùng .filter
trong code iOS trước đây nhiều rồi. Và nó cũng có ý nghĩa tương tự như vậy à. Nhưng phần hay còn rất nhiều ở sau.
1. Ignoring operators
1.1. ignoreElements
Khi cuộc đời này không có gì vui và bạn không có tâm sự để đón nhận tất cả các elements từ một Observable nào đó mà bạn đã đăng ký tới, thì hãy dùng toán tử ignoreElements
. Tuy nhiên, nó sẽ cho phép nhận các sự kiện .completed
& .error
.
Rất đơn giản phải không nào. Dễ hiểu hơn nữa thì hãy xem đoạn code sau.
let subject = PublishSubject<String>() let bag = DisposeBag() subject .ignoreElements() .subscribe { event in print(event) } .disposed(by: bag) subject.onNext("1") subject.onNext("2") subject.onNext("3") subject.onCompleted()
Khi bạn thực thi đoạn code trên, thì chỉ thấy nhận được mỗi completed
. Và trong khi gõ code, Xcode sẽ suggestion một function subscribe
với chỉ 2 tham số onCompleted
và onError
.
1.2. elementAt(_:)
Toán tử tiếp theo sẽ giúp bạn lấy đúng phần tử thứ n
nào đó. Và theo truyền thống lập trình, việc đếm sẽ bắt đầu từ 0
. Sau đây là code ví dụ cho bạn. À, bạn chỉ cần thay toán tử cho đoạn code trên là được.
let subject = PublishSubject<String>() let bag = DisposeBag() subject .elementAt(2) .subscribe { event in print(event) } .disposed(by: bag) subject.onNext("1") subject.onNext("2") subject.onNext("3") subject.onCompleted()
Kết quả thực thi như sau:
next(3) completed
Tất nhiên, 2 sự kiện completed
và error
thì vẫn nhận được. Và khi bạn xoá dòng code subject.onCompleted()
này đi thì vẫn thấy kết quả như trên. Mặc dù subject
của bạn lúc đó không hề kết thúc.
Từ đó, một điều thú vị nữa là:
Khi toán tử
elementAt
này giúp bạn đạt được mục đích của nó, thì subscription sẽ tự động kết thúc.
1.3. filter{ }
Khi bạn có quá nhiều phần tử cần phải lấy, thì 2 toán tử trên sẽ không đảm bảo cho bạn đạt được mục đích của mình. Giờ chúng ta sẽ sử dụng toán tử filter
để có thể lấy nhiều phần tử mà mình mong muốn.
Bạn xem qua đoạn code sau:
let bag = DisposeBag() let array = Array(0...10) Observable.from(array) .filter { $0 % 2 == 0 } .subscribe(onNext: { print($0) }) .disposed(by: bag)
Trong đó:
array
là 1 mãng Int với các giá trị từ 0 đến 10.from
để tạo ra 1 Observable và phát ra các giá trị lần lượt trong array trên.filter
với điều kiện lấy những giá trị chẵn
Tuỳ thuộc vào yêu cầu bài toán của bạn thì sẽ có điều kiện khác để phù hợp.
2. Skip Operators
2.1. skip(_:)
Đại diện cho nhóm toán tử này là toán tử skip
. Với 1 tham số truyền vào cho nó, là số lượng các phần tử bị bỏ đi. Và subscriber sẽ không nhận chúng. Subscriber sẽ nhận các phát ra từ thứ n
cho đến khi Observable kết thúc.
let disposeBag = DisposeBag() Observable.of("A", "B", "C", "D", "E", "F") .skip(3) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag)
Subscription sẽ bắt đầu từ giá trị lần thứ 3
phát đi cho đến khi Observable kết thúc.
2.2. skipWhile { }
Toán tử này hơn hại não một chút. Nó cũng tương tự như là filter
. Tuy nhiên, nó có một số đặc điểm sau:
- Sẽ bỏ qua các phần tử mà thoả mãn điều kiện của nó, tức là
true
. - Từ phần tử đầu tiên không thoả điều kiện (tức là
false
) thì kết thúc quá trình lọc và bắt đầu nhận giá trị. - Các phần tử tiếp theo sau đó vẫn được nhận (đây là điểm khác với filter).
let bag = DisposeBag()
Observable.of(2, 4, 8, 9, 2, 4, 5, 7, 0, 10)
.skipWhile { $0 % 2 == 0 }
.subscribe(onNext: {
print($0) })
.disposed(by: bag)
Kết quả thực thi như sau:
9
2
4
5
7
0
10
Bạn sẽ thấy 3 phần tử đầu tiên thoả mãn điều kiện là chẵn thì sẽ bị bỏ qua. Trong khi, các phần tử chẵn phát ra sau khi điệu kiện đã sai thì vẫn nhận được.
2.3. skipUntil(_:)
Toàn bộ toán tử trên là bạn đã lọc hoặc bỏ qua các phần tử với các kiều kiện tĩnh. Còn với skipUntil
thì sẽ sử dụng một điều kiện động. Ở đây chính là dùng một observable
khác để làm điều kiện.
Code ví dụ demo như sau:
let bag = DisposeBag() let subject = PublishSubject<String>() let trigger = PublishSubject<String>() subject .skipUntil(trigger) .subscribe(onNext: { value in print(value) }) .disposed(by: bag) subject.onNext("1") subject.onNext("2") subject.onNext("3") subject.onNext("4") subject.onNext("5") trigger.onNext("XXX") subject.onNext("6") subject.onNext("7")
Tạo ra 2 subject. Sử dụng toán tử skipUntil
với tham số là subject kia. Thì subscription sẽ vẫn chờ cho tới khi trigger
phát ra giá trị đầu tiên. Thì các giá trị của subject
sau đó phát đi thì mới nhận được.
OKAY! EZ phải không nào! Bạn có thể lợi dụng các loại toán tử liên quan tới điều kiện động trong nhóm Filtering Operators như là một trigger để thực thi các logic riêng của bạn trong project.
3. Taking Operators
Nhóm toán tử này sẽ có ý nghĩa ngược lại với nhóm skip. Cùng nhau tìm hiểu tiếp nào!
3.1. take(_:)
Toán tử take
này cần 1 tham số là Int. Nó sẽ quy định số lượng phần tử cần lấy từ Observable phát đi. Nếu đủ số lượng thì sẽ tự động kết thúc.
Code ví dụ như sau:
let bag = DisposeBag() Observable.of(1, 2, 3, 4, 5, 6, 7, 8, 9) .take(4) .subscribe(onNext: { (value) in print(value) }) .disposed(by: bag)
Kết quả thực thi:
1 2 3 4
Chỉ nhận được 4 giá trị đầu tiên. Các giá trị sau sẽ không nhận được.
3.2. takeWhile { }
Toán tử này sẽ giúp bạn lấy hết những giá trị đầu tiên mà thoả mãn điều kiện. Cho tới khi phần tử đầu tiên không thoả mãn điều kiện, thì từ đó và tất cả các phần tử sau sẽ không nhận được.
let bag = DisposeBag() Observable.of(1, 2, 3, 4, 5, 6, 7, 8, 9) .takeWhile { $0 < 4 } .subscribe(onNext: { (value) in print(value) }) .disposed(by: bag)
Vẫn là ví dụ trên của take
, nhưng chúng ta thay đổi toán tử thành takeWhile
với điều kiện các phân tử < 4
thì sẽ nhận được 3
phần tử đầu tiên phát đi.
enumerated()
Một sự kết hợp xịn xò hơn nữa là với toán tử enumerated
. Nó sẽ thêm index
cho giá trị của bạn. Element mới là sự kết hợp của index
với giá trị của element
của Observable tạo nên 1 cặp Tuple.
Xem ví dụ sau với điều kiện được nâng cấp hơn.
let bag = DisposeBag() Observable.of(2, 4, 6, 8, 0, 12, 1, 3, 4, 6, 2) .enumerated() .takeWhile { index, value in value%2 == 0 && index < 3 } .subscribe(onNext: { (value) in print(value) }) .disposed(by: bag)
Sử dụng 2 điều kiện là:
- Các giá trị nhận được là số chẵn
- Nằm trong 3 giá trị đầu tiên
Hi vọng nó sẽ giúp bạn thêm vũ khí khi kết hợp với các toán tử còn lại trong nhóm Filtering Operators này.
3.3. takeUntil(_:)
Đây là phiên bản đối nghịch với skipUntil
. Nên cũng dễ hiểu thôi. Nó giúp lấy tất cả các phần tử đầu tiên thoả mãn điều kiện động. Tiếp tục tham khảo đoạn code ví dụ sau:
let bag = DisposeBag() let subject = PublishSubject<String>() let trigger = PublishSubject<String>() subject .takeUntil(trigger) .subscribe(onNext: { value in print(value) }) .disposed(by: bag) subject.onNext("1") subject.onNext("2") subject.onNext("3") subject.onNext("4") subject.onNext("5") trigger.onNext("XXX") subject.onNext("6") subject.onNext("7")
Sử dụng lại ví dụ của skipUntil
nhưng thay bằng toán tử takeUntil
, bạn sẽ nhận được tất cả các giá trị trước khi trigger
phát ra giá trị đầu tiên.
4. Distinct operators
4.1. Equatable Type
Để bắt đầu cho toán tử mới này thì bạn xem code sau:
let disposeBag = DisposeBag() Observable.of("A", "A", "B", "B", "A", "A", "A", "C", "A") .distinctUntilChanged() .subscribe(onNext: { print($0) }) .disposed(by: disposeBag)
Kết quả thực thi như sau:
A B A C A
So với array đầu vào, bạn sẽ nhận thấy một điều là các phần tử giống nhau liên tiếp thì sẽ bị loại bỏ. Như vậy với 2 giá trị A
thì sẽ giữ lại 1. Với 3 giá trị A
liên tiếp thì giữ lại 1.
Toán tử distinctUntilChanged
sẽ thanh lọc các giá trị trùng nhau liên tiếp trong chuỗi các giá trị được phát đi.
4.2. Custom Type
Nhưng ở trên là áp dụng cho các kiểu dữ liệu có thể so sánh được. Hay còn gọi là Equatable Type
. Còn với các Custom Type
thì như thế nào.
Ta có ví dụ code sau:
struct Point { var x: Int var y: Int } let disposeBag = DisposeBag() let array = [ Point(x: 0, y: 1), Point(x: 0, y: 2), Point(x: 1, y: 0), Point(x: 1, y: 1), Point(x: 1, y: 3), Point(x: 2, y: 1), Point(x: 2, y: 2), Point(x: 0, y: 0), Point(x: 3, y: 3), Point(x: 0, y: 1)] Observable.from(array) .distinctUntilChanged { (p1, p2) -> Bool in p1.x == p2.x } .subscribe(onNext: { point in print("Point (\(point.x), \(point.y))") }) .disposed(by: disposeBag)
Trong đó
- Định nghĩa ra 1 Struct
Point
với 2 thuộc tính làx
&y
. - Tạo 1
array
với các phân tử là Point. Bạn để ý thì sẽ thấy các point trong array đó. Thì có vài point liên tiếp cóx
bằng nhau. - Tạo ra
Observable
từ array trên và phát đi liên tiếp các giá trị của array. distinctUntilChanged
lúc này chọn là 1 biểu thức với giá trị trả về từ 1 closue.- Trong closure đó ta sẽ so sánh
x
của 2 point liên tiếp nhau. Nếu chúng bằng nhau thì bỏ qua phần tử tiếp theo đó. subscribe
như trước đây
Vì Struct Point lúc này không thể so sánh được với nhau, nên chúng ta sẽ so sánh với 1 thuộc tính của chúng. Kết quả thực thi ra như sau:
Point (0, 1) Point (1, 0) Point (2, 1) Point (0, 0) Point (3, 3) Point (0, 1)
Bây giờ, bạn chã sợ bố con nhà thèn nào nữa nha! Cũng tới 1 kho vũ khí hạt nhân dành cho bạn rồi đó. OKAY! và mình xin hết thúc bài viết về nhóm toán tử Filtering Operators tại đây.
Nếu có gì thắc mắc hay góp ý cho mình thì bạn có thể để lại bình luận hoặc gởi email theo trang Contact. Hẹn gặp lại bạn ở các phần tiếp theo của các nhóm toán tử trong RxSwift.
Tạm kết
Mình sẽ tóm tắt lại ý nghĩa các toán tử trong nhóm Filtering Operators nha.
ignoreElements
: từ chối nhận tất cả các phần tử phát ra từ Observable. Chỉ nhậncompleted
&error
.elementAt(_:)
lấy đúng phần tử thứn
được phát đi trong sequence observable.filter{ }
lọc tất cả phần tử trong sequence observable phát đi và lấy các phần tử thoả mãn điều kiện. Việc duyệt này sẽ duyệt cho tới khi Observable kết thúc.skip(_:)
bỏ quan
phần tử đầu tiên được phát đi.skipWhile { }
bỏ qua tất cả phần tử đầu tiên mà thoả mãn điều kiện. Từ phần tử đầu tiên không thoả mãn điều kiện thì sẽ nhận tất cả cho đến khi Observable kết thúc.skipUntil(_:)
tương tự với skipWhile nhưng điều kiện lúc này là mộtobservable
. Khi nào Observable đó phát ra giá trị đầu tiên thì vòng lặp kết thúc.take(_:)
lấyn
phần tử đầu tiên được phát đi. Các phần tử sau đó sẽ không nhận.takeWhile { }
chấp nhận tất cả các phần tử đầu tiên mà thoả mãn điều kiện. Từ phần tử đầu tiên không thoả mãn điều kiện trở đi cho đến khi Observable kết thúc, thì sẽ auto không nhận.takeUntil(_:)
như skipUntil nhưng sẽ ngược lại. Nhận các phần tử đầu tiên được Observable phát đi cho tới khi Observable trigger phát thì các phần tử sau đó sẽ không nhận..distinctUntilChanged()
loại trừ đi các phần tử giống nhau liên tiếp về mặt giá trị & giữ lại 1 giá trị đại diện thôi. Dùng được cho cả Equatable Type & Custom Type.enumerated()
thêm index cho các giá trị phát đi của Observable. Kiểu giá trị mới mà subscriber nhận được là 1 Tuple(index, element)
.
Cảm ơn bạn đã đọc bài viết này!
Related Posts:
Written by chuotfx
Hãy ngồi xuống, uống miếng bánh và ăn miếng trà. Chúng ta cùng nhau đàm đạo về đời, về code nhóe!
2 comments
Leave a Reply Cancel reply
Fan page
Tags
Recent Posts
- Complete Concurrency với Swift 6
- 300 Bài code thiếu nhi bằng Python – Ebook
- Builder Pattern trong 10 phút
- Observer Pattern trong 10 phút
- Memento Pattern trong 10 phút
- Strategy Pattern trong 10 phút
- Automatic Reference Counting (ARC) trong 10 phút
- Autoresizing Masks trong 10 phút
- Regular Expression (Regex) trong Swift
- Lập trình hướng giao thức (POP) với Swift
You may also like:
Archives
- July 2024 (1)
- June 2024 (1)
- May 2024 (4)
- April 2024 (2)
- March 2024 (5)
- January 2024 (4)
- February 2023 (1)
- January 2023 (2)
- November 2022 (2)
- October 2022 (1)
- September 2022 (5)
- August 2022 (6)
- July 2022 (7)
- June 2022 (8)
- May 2022 (5)
- April 2022 (1)
- March 2022 (3)
- February 2022 (5)
- January 2022 (4)
- December 2021 (6)
- November 2021 (8)
- October 2021 (8)
- September 2021 (8)
- August 2021 (8)
- July 2021 (9)
- June 2021 (8)
- May 2021 (7)
- April 2021 (11)
- March 2021 (12)
- February 2021 (3)
- January 2021 (3)
- December 2020 (3)
- November 2020 (9)
- October 2020 (7)
- September 2020 (17)
- August 2020 (1)
- July 2020 (3)
- June 2020 (1)
- May 2020 (2)
- April 2020 (3)
- March 2020 (20)
- February 2020 (5)
- January 2020 (2)
- December 2019 (12)
- November 2019 (12)
- October 2019 (19)
- September 2019 (17)
- August 2019 (10)
3.2. takeWhile { }
Vẫn là ví dụ trên của take, nhưng chúng ta thay đổi toán tử thành takeWhile với điều kiện các phân tử < 4 thì sẽ nhận được 4 phần tử đầu tiên phát đi.
Chỗ này phải là nhận được 3 phần tử đầu tiên chứ anh
Thanks em! Chỗ này mình ghi sai rồi. 3 phần tử mới đúng.