Combining Operators

StartWith

startWith는 특정 시퀀스의 맨 앞에 엘리먼트를 추가해버리는 것이다.

예를 들어 다음과 같은 시퀀스가 있다고 하자.

 

 

 

이 경우 startWith(1) 을 호출하는 경우 아래 그림처럼 시퀀스의 맨 앞에 1이 추가된다.

 

 

예제

예를 들어 다음과 같은 코드가 있다고 하면

numbers는 2,3,4로 이루어져 있고 거기에 startWith(1)을 호출했으므로

맨 앞에 1이 붙어서 출력은 1, 2, 3, 4가 되는 것이다.

let disposeBag = DisposeBag()

let numbers = Observable.of(2,3,4)

let observable = numbers.startWith(1)
observable.subscribe(onNext: {
    print($0)
}).disposed(by: disposeBag)

/* 출력
1
2
3
4
*/

 

concat

concat은 두 가지 이상의 시퀀스를 하나로 합치고 싶을 때 사용하는 개념이다.

예를 들어 다음과 같은 두 가지 시퀀스가 있다고 할 때,

 

 

 

concat([first, second])의 호출은 아래 그림으로 나타난다.

 

 

 

예제

코드를 예로 들어 설명하자면 다음과 같다.

first, second를

let disposeBag = DisposeBag()

let first = Observable.of(1,2,3)
let second = Observable.of(4,5,6)

let observable = Observable.concat([first, second])

observable.subscribe(onNext: {
    print($0)
}).disposed(by: disposeBag)

 

이 경우 출력은 예상하겠지만 다음처럼 된다:

1
2
3
4
5
6

 

Merge

예를 들어 다음과 같은 두 개의 시퀀스가 있다고 하자.

Left, right는 각각 [1,2,3],[4,5,6]을 갖지만

발생한 시간순으로 보면 1,4,5,2,6,3의 순을 가진다.

 

이 때 merge는 해당 순서를 지켜가면서 두 가지 요소를 합친다.

즉, left + right는 아래 그림처럼 되는 것이다.

 

 

 

예제

예제를 통해서도 살펴보자.

left와 right는 Int를 publish하는 Subject이고,

subscribe를 위해 observable하게 만들어주었다.

그런 다음 left와 right를 merge하고,

merge한 결과를 subscribe한 뒤 어떤 이벤트가 발생하면 바로 그것을 출력하도록 만들었다.

 

let disposeBag = DisposeBag()

let left = PublishSubject<Int>()
let right = PublishSubject<Int>()

let source = Observable.of(left.asObservable(), right.asObservable())

let observable = source.merge()

observable.subscribe(onNext: {
    print($0)
}).disposed(by: disposeBag)

left.onNext(1)
right.onNext(3)
left.onNext(2)
right.onNext(4)

 

이 경우 left와 right의 onNext 호출은 항상 순서를 지켜가며 출력을 한다.

그러니 위의 코드 또한 다음 출력값처럼 나타나는 것이다:

 

1
3
2
4

 

combineLatest

combineLatest는 어떤 두 시퀀스를 합치되 두 가지 시퀀스 모두에서 최소 하나의 값이 들어왔을 때

가장 최신의 값을 가져오는 것이다.

예를 들어 다음과 같은 시퀀스들이 있다고 치면,

 

 

위 두가지 시퀀스를 combineLatest한 결과는 다음과 같다:

left 1 1 2 2 3
right 4 5 5 6 6

 

예제를 통해서도 살펴보자.

 

예제

예를 들어 다음과 같은 코드가 있다고 하자.

left와 right의 combineLatest는 lastOfLeft 및 lastOfRight 두 가지 결과로 나타나고,

"\(lastOfLeft) \(lastOfRight)" 와 같은 형태로 표현될 수 있게 return 해주었다.

(이 때 return 한 값의 자료형은 Observable이다.)

그리고 그것을 subscribe하고 차례로

left부터 1,2,3

right4,

left 5,

right 6,7 을 호출했다.

let disposeBag = DisposeBag()

let left = PublishSubject<Int>()
let right = PublishSubject<Int>()

let observable = Observable.combineLatest(left, right, resultSelector: {
    lastOfLeft, lastOfRight in
    return "\(lastOfLeft) \(lastOfRight)"
}).subscribe(onNext: {
    print($0)
})

left.onNext(1)
left.onNext(2)
left.onNext(3)
right.onNext(4)
left.onNext(5)
right.onNext(6)
right.onNext(7)

 

결과는 다음과 같다.

호출되는 시점은 combine 대상 모두가 값을 가지고 있을 때이기 때문에,

맨 처음 left.onNext(1, 2)에 대해서는 right에 값이 없어 아무것도 출력되지 않았다.

3 4
5 4
5 6
5 7

 

withLatestFrom

withLatestFrom은 예를 들어 아래 예제가 있다고 치면

button.withLatestFrom(textField) 에 대해

textField에 par -> pari -> paris 순으로 입력된 후

button이 두 번 클릭되었다고 하면

 

 

 

아래 처럼 두 번 모두 textField에 입력된 최신 값인 paris를 가져오는 것이다.

 

 

예제

위에서 설명한 대로 이해하고 아래를 살펴보면 출력값이 어떻게 될 것 같은가?

아마도 sw, swi 는 무시되고 swift 만 두 번 출력될 것이라고 예상했을 것이다.

 

let disposeBag = DisposeBag()

let button = PublishSubject<Void>()
let textField = PublishSubject<String>()

let observable = button.withLatestFrom(textField)
let disposable = observable.subscribe(onNext: {
    print($0)
}).disposed(by: disposeBag)

textField.onNext("sw")
textField.onNext("swif")
textField.onNext("swift")

button.onNext(()) //empty function ()
button.onNext(())

 

결과는 우리 생각이 맞았다우우으예우~~

swift
swift

 

reduce

일단 reduce는 두 가지 인자를 받는다.

하나는 초기값, 그리고 나머지 하나는 연산자 +, -, /, %, * 중 하나이다.

만약 아래와 같이 1,2,3 이라는 요소가 있다고 할 때,

reduce(0, accumulator: +) 는 초기값 0에 1, 2, 3 모두를 더해 총 6이라는 결과를 나타낸다.

 

 

 

 

예제

아래는 위에서 설명한 + 연산에 대한 예제이다. 이것을 +를 제외한 -, /, %, * 도 적용하여 실험해보자.

let disposeBag = DisposeBag()


let source = Observable.of(1,2,3)

source.reduce(0, accumulator: +)
    .subscribe(onNext: {
        print($0)
    }).disposed(by: disposeBag)
// 출력값: 6

 

accumulator는 커스텀해서 사용할 수 있다.

무슨 말이냐면 accumulator +는 사실상 다음과 같이도 표현할 수 있어서,

좀더 복잡한 식을 넣을 가능성을 열어놓았다.

let disposeBag = DisposeBag()

let source = Observable.of(1,2,3)

source.reduce(0, accumulator: {
  summary, newValue in
  return summary + newValue
}).subscribe(onNext: {
  print($0)
}).disposed(by: disposeBag)

 

위 경우에 summary는 여태까지 accumulate 된 값이고,

newValue는 1, 2, 3 각각의 요소가 된다.

 

scan

scan은 reduce랑 거의 동일하다.

다만 reduce는 결과만을 리턴하지만 scan은 모든 과정을 전부 리턴한다.

아래를 reduce하면 6만 리턴하겠지만

scan을 통한다면 1, 3, 6이 리턴되는 것처럼.

 

 

예제

아래는 1~6까지 더하는 과정을 모두 출력해내고 있다.

let disposeBag = DisposeBag()

let source = Observable.of(1,2,3,4,5,6)

source.scan(0, accumulator: +)
    .subscribe(onNext: {
      print($0)
    }).disposed(by: disposeBag)

/* 출력
 1
 3
 6
 10
 15
 21
 */
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기
// custom