+2

Bài 10. RxSwift – Filtering Operators

1. Ignoring operators

1.1. ignoreElements

Khi bạn không muố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. Ví dụ:

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

Kết quả:

completed

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. Ví dụ:

  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ả:

next(3)
completed

Tất nhiên, 2 sự kiện completederror 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.

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. Ví dụ:

    let bag = DisposeBag()
    let array = Array(0...10)
    
    Observable.from(array)
        .filter { $0 % 2 == 0 }
        .subscribe(onNext: {
            print($0) })
        .disposed(by: bag)

Kết quả:

0
2
4
6
8
10
  • 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

2. Skip Operators

2.1. 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. Ví dụ:

    let disposeBag = DisposeBag()
    
    Observable.of("A", "B", "C", "D", "E", "F")
        .skip(3) 
        .subscribe(onNext: {
            print($0) })
        .disposed(by: disposeBag)

Kết quả:

D
E
F

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. skip(while: _ )

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

Ví dụ:

    let bag = DisposeBag()
    
    Observable.of(2, 4, 8, 9, 2, 4, 5, 7, 0, 10)
        .skip(while: { $0 % 2 == 0 })
        .subscribe(onNext: {
            print($0) })
        .disposed(by: bag)

Kết quả:

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. skip(until: _ )

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. Ví dụ:

  let bag = DisposeBag()
    
    let subject = PublishSubject<String>()
    let trigger = PublishSubject<String>()
    
    subject
        .skip(until: 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")

Kết quả:

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

3. Taking Operators

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. Ví dụ:

  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ả:

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. take(while: _ )

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. Ví dụ:

 Observable.of(1, 2, 3, 4, 5, 6, 7, 8, 9).take(while: {$0 < 4})
            .subscribe(onNext: { (value) in
                print(value)
            })
            .disposed(by: bag)

Kết quả:

1
2
3

Vẫn là ví dụ trên của take, nhưng chúng ta thay đổi toán tử thành take(while:) 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()

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. ví dụ:

      Observable.of(2, 4, 6, 8, 0, 12, 1, 3, 4, 6, 2)
            .enumerated()
            .take(while: { index, value in
                value%2 == 0 && index < 3
            })
            .subscribe(onNext: { (value) in
                print(value)
            })
            .disposed(by: bag)

Kết quả:

(index: 0, element: 2)
(index: 1, element: 4)
(index: 2, element: 6)

3.3. take(until: _ )

Đâ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")

Kết quả:

1
2
3
4
5

4. Distinct operators

4.1. Equatable Type

Ví dụ:

 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ả:

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

Ví dụ:

 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)
  • Đị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 đó.

Kết quả:

Point (0, 1)
Point (1, 0)
Point (2, 1)
Point (0, 0)
Point (3, 3)
Point (0, 1)

Tham khảo:

https://fxstudio.dev/rxswift-filtering-operators/


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí