เริ่มต้น Reactive Programming ด้วย Combine Part7- Mapping Operator

Cocodev
Lotus’s IT
Published in
3 min readDec 18, 2023

Mapping operator เป็น operator ของ publisher สำหรับ transforms data จาก stream เดิมไปยัง stream ใหม่

numbersA:  |----5----4----3----2----1----0------------------------|-->
opratoion: .map { $0 * 2 }
numbersB: |---10----8----6----4----2----0------------------------|-->


time(s): |----|----|----|----|----|----|----|----|----|----|----|-->
0 1 2 3 4 5 6 7 8 9 10 11

1. map(_:)

การ transforms data ทุกๆ elements จาก publisher ไปเป็นอีก publisher หนึ่ง

func map<T>(_ transform: @escaping (Self.Output) -> T) -> Publishers.Map<Self, T>

example

let numbers = [5, 4, 3, 2, 1, 0]
let romanNumeralDict: [Int : String] =
[1:"I", 2:"II", 3:"III", 4:"IV", 5:"V"]
cancellable = numbers.publisher
.map { romanNumeralDict[$0] ?? "(unknown)" }
.sink { print("\($0)", terminator: " ") }


// Prints: "V IV III II I (unknown)"

2. tryMap(_:)

การ transforms data ทุกๆ elements และรองรับการ map ที่มีการ throws error จาก publisher

func tryMap<T>(_ transform: @escaping (Self.Output) throws -> T) -> Publishers.TryMap<Self, T>

example

struct ParseError: Error {}
func romanNumeral(from:Int) throws -> String {
let romanNumeralDict: [Int : String] =
[1:"I", 2:"II", 3:"III", 4:"IV", 5:"V"]
guard let numeral = romanNumeralDict[from] else {
throw ParseError()
}
return numeral
}
let numbers = [5, 4, 3, 2, 1, 0]
cancellable = numbers.publisher
.tryMap { try romanNumeral(from: $0) }
.sink(
receiveCompletion: { print ("completion: \($0)") },
receiveValue: { print ("\($0)", terminator: " ") }
)


// Prints: "V IV III II I completion: failure(ParseError())"

3. mapError(_:)

การ transforms error เดิมให้เป็น error ใหม่ จาก publisher

func mapError<E>(_ transform: @escaping (Self.Failure) -> E) -> Publishers.MapError<Self, E> where E : Error

example

struct DivisionByZeroError: Error {}
struct MyGenericError: Error { var wrappedError: Error }


func myDivide(_ dividend: Double, _ divisor: Double) throws -> Double {
guard divisor != 0 else { throw DivisionByZeroError() }
return dividend / divisor
}


let divisors: [Double] = [5, 4, 3, 2, 1, 0]
divisors.publisher
.tryMap { try myDivide(1, $0) }
.mapError { MyGenericError(wrappedError: $0) }
.sink(
receiveCompletion: { print ("completion: \($0)") ,
receiveValue: { print ("value: \($0)", terminator: " ") }
)


// Prints: "0.2 0.25 0.3333333333333333 0.5 1.0 completion: failure(MyGenericError(wrappedError: DivisionByZeroError()))"

4. replaceNil(with:)

คือการ replace ค่า nil ในทุกๆ elements จาก publisher

func replaceNil<T>(with output: T) -> Publishers.Map<Self, T> where Self.Output == T?

example

let numbers: [Double?] = [1.0, 2.0, nil, 3.0]
numbers.publisher
.replaceNil(with: 0.0)
.sink { print("\($0)", terminator: " ") }


// Prints: "Optional(1.0) Optional(2.0) Optional(0.0) Optional(3.0)"

5. scan(_:_:)

การ transforms ทุก elements จาก publisher โดยค่าล่าสุด กับ ปัจจุบัน มี initialResult เป็น parameters สำหรับค่าเริ่มต้น

func scan<T>(
_ initialResult: T,
_ nextPartialResult: @escaping (T, Self.Output) -> T
) -> Publishers.Scan<Self, T>

example การหาผลบวกของแต่ละ elements

let range = (0...5)
cancellable = range.publisher
.scan(0) { return $0 + $1 }
.sink { print ("\($0)", terminator: " ") }
// Prints: "0 1 3 6 10 15 ".

6. tryScan(_:_:)

การ transforms ทุก elements จาก publisher โดยค่าล่าสุด กับ ปัจจุบัน และ Error มี initialResult เป็น parameters สำหรับค่าเริ่มต้น

func tryScan<T>(
_ initialResult: T,
_ nextPartialResult: @escaping (T, Self.Output) throws -> T
) -> Publishers.TryScan<Self, T>

example

struct DivisionByZeroError: Error {}


/// A function that throws a DivisionByZeroError if `current` provided by the TryScan publisher is zero.
func myThrowingFunction(_ lastValue: Int, _ currentValue: Int) throws -> Int {
guard currentValue != 0 else { throw DivisionByZeroError() }
return (lastValue + currentValue) / currentValue
}


let numbers = [1,2,3,4,5,0,6,7,8,9]
cancellable = numbers.publisher
.tryScan(10) { try myThrowingFunction($0, $1) }
.sink(
receiveCompletion: { print ("\($0)") },
receiveValue: { print ("\($0)", terminator: " ") }
)


// Prints: "11 6 3 1 1 -1 failure(DivisionByZeroError())".

--

--