Skip to content
  • Home
  • Code
  • iOS & Swift
  • Combine
  • RxSwift
  • SwiftUI
  • Flutter & Dart
  • Tutorials
  • Art
  • Blog
Fx Studio
  • Home
  • Code
  • iOS & Swift
  • Combine
  • RxSwift
  • SwiftUI
  • Flutter & Dart
  • Tutorials
  • Art
  • Blog
Written by chuotfx on September 19, 2020

RxSwift – Filtering Operators

RxSwift

Contents

  • Chuẩn bị
  • Filtering Operators
  • 1. Ignoring operators
    • 1.1. ignoreElements
    • 1.2. elementAt(_:)
    • 1.3. filter{ }
  • 2. Skip Operators
    • 2.1. skip(_:)
    • 2.2. skipWhile { }
    • 2.3. skipUntil(_:)
  • 3. Taking Operators
    • 3.1. take(_:)
    • 3.2. takeWhile { }
      • enumerated()
    • 3.3. takeUntil(_:)
  • 4. Distinct operators
    • 4.1. Equatable Type
    • 4.2. Custom Type
  • Tạm kết

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:

    • RxSwift – Tìm hiểu Operators & Hello world!

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ận completed & 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ỏ qua n 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ột observable. Khi nào Observable đó phát ra giá trị đầu tiên thì vòng lặp kết thúc.
  • take(_:) lấy n 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!

FacebookTweetPinYummlyLinkedInPrintEmailShares4

Related Posts:

  • RxSwift - Transforming Operators
    RxSwift - Transforming Operators
  • RxSwift vs. UIKit - Hello ViewController
    RxSwift vs. UIKit - Hello ViewController
  • RxSwift - Relays
    RxSwift - Relays
  • RxSwift - Tìm hiểu Operators & Hello world!
    RxSwift - Tìm hiểu Operators & Hello world!
Tags: rxswift
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

  • Duy has written: April 30, 2021 at 5:59 pm Reply

    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

    • chuotfx has written: May 5, 2021 at 1:13 am Reply

      Thanks em! Chỗ này mình ghi sai rồi. 3 phần tử mới đúng.

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Donate – Buy me a coffee!

Fan page

Fx Studio

Tags

Actor Advanced Swift api AppDistribution Asynchronous autolayout basic ios tutorial blog callback ci/cd closure collectionview combine concurrency CoreData Core Location crashlytics darkmode dart dart basic dart tour Declarative decoding delegate deploy fabric fastlane firebase flavor flutter GCD iOS mapview MVVM optional protocol rxswift Swift Swift 5.5 SwiftUI SwiftUI Notes tableview testing TravisCI unittest

Recent Posts

  • Raw String trong 10 phút
  • Dispatch Semaphore trong 10 phút
  • Tổng kết năm 2022
  • KeyPath trong 10 phút – Swift
  • Make color App Flutter
  • Ứng dụng Flutter đầu tiên
  • Cài đặt Flutter SDK & Hello world
  • Coding Conventions – người hùng hay kẻ tội đồ?
  • Giới thiệu về Flutter
  • Tìm hiểu về ngôn ngữ lập trình Dart

You may also like:

  • RxSwift - Transforming Operators
    RxSwift - Transforming Operators
  • RxSwift - Hello Subjects
    RxSwift - Hello Subjects
  • RxSwift vs. UIKit – Tương tác giữa các ViewController
    RxSwift vs. UIKit – Tương tác giữa các ViewController
  • RxSwift vs. UIKit - Fetching Data from API
    RxSwift vs. UIKit - Fetching Data from API
  • RxSwift - Publish Subjects
    RxSwift - Publish Subjects

Archives

  • 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)

About me

Education, Mini Game, Digital Art & Life of coders
Contacts:
contacts@fxstudio.dev

Fx Studio

  • Home
  • About me
  • Contact us
  • Mail
  • Privacy Policy
  • Donate
  • Sitemap

Categories

  • Art (1)
  • Blog (22)
  • Code (4)
  • Combine (22)
  • Flutter & Dart (24)
  • iOS & Swift (86)
  • RxSwift (37)
  • SwiftUI (76)
  • Tutorials (70)

Newsletter

Stay up to date with our latest news and posts.
Loading

    Copyright © 2023 Fx Studio - All rights reserved.

    Share this ArticleLike this article? Email it to a friend!

    Email sent!