Leave a rating/review
Filtering opertors consume values from a publisher, and conditionally decide whether to pass them onto the consumer. There are quite a few filtering operators to talk about, and even variations on those operators, so I’ve already run my own filtering operator to narrow them down to a set to go over here. Let’s get started with some filtering basics.
Let’s start with the aptly named filter operator, which takes a predicate to determine if the value gets passed on to the consumer. The marble diagram here shows that a series of numbers - 1 through 10 - are sent via the publisher, but the predicate only returns true for those that are multiples of 3, leaving 3, 6, and 9. Only those 3 values are sent to the consumer on the bottom line. Let’s see how that looks in a playground.
example(of: "filter") {
// 1
let numbers = (1...10).publisher
// 2
numbers
.filter { $0.isMultiple(of: 3) }
.sink(receiveValue: { n in
print("\(n) is a multiple of 3!")
})
.store(in: &subscriptions)
}
Here you: create a new publisher, which will emit a finite number of values — 1 through 10, and then complete, using the publisher property on Sequence types. Use the filter operator, passing in a predicate where you only allow through numbers that are multiples of three.
Sometimes you may want to filter duplicate values coming in from your publisher before sending them downstream. That’s easy enough to do with the removeDuplicates operator. Implementing this operator is really easy. You don’t need to pass any arguments, as long as the values in the stream conform to Equatable. Let’s look at that in a demo.
example(of: "removeDuplicates") {
// 1
let words = "hey hey there! want to listen to mister mister ?"
.components(separatedBy: " ")
.publisher
// 2
words
.removeDuplicates()
.sink(receiveValue: { print($0) })
.store(in: &subscriptions)
}
The use of this operator is straightforward - insert the operator between the publisher and a subscriber, and just like that - duplicates get filtered out!
Some publishers can emit optional values, or even just return nils as their result - but what if you don’t want those values? Those of you familar with the Swift standard library may recognize compactMap as a solution here, and as luck would have it, there is an operator that does that same thing!
The marble diagram here shows that the values on the incoming stream that can be cast as Floats get passed onto the consumer, but those that can’t - like the letter “a” - get filtered out. Let’s look at that in the playground.
example(of: "compactMap") {
// 1
let strings = ["a", "1.24", "3",
"def", "45", "0.23"].publisher
// 2
strings
.compactMap { Float($0) }
.sink(receiveValue: {
// 3
print($0)
})
.store(in: &subscriptions)
}
Here you: create a publisher that emits a finite list of strings; use compactMap to attempt to initialize a float with those strings. If Float’s initializer can’t do that, it returns nil, and each nil is filtered out; the sink subscriber receives and prints only the correctly initialized floats.
There may be cases where you just want to ignore all of the values from a publisher, only being concerned when the completion event is sent. The ignoreOutput operator does this for you, as shown in the marble diagram. Only the completion event is passed downstream. Let’s see how this looks in code.
example(of: "ignoreOutput") {
// 1
let numbers = (1...10_000).publisher
// 2
numbers
.ignoreOutput()
.sink(receiveCompletion: { print("Completed with: \($0)") },
receiveValue: { print($0) })
.store(in: &subscriptions)
}
Here, you initialize a publisher that emits values 1 through 10,000. However, you attach the ignoreOperator to that publisher and a sink subscriber onto the end of the pipeline. There’s no real surprise here that the only thing printed when you run the
playground is “Completed with: finished”.
That covers some of the basics of Filtering operators. You have learned the basic filter operator, which uses a predicate to exclude values from getting sent to the consumer.
removeDuplicates and ignoreOutput, which do exactly what they say they do - remove duplicates from the stream and ignore output from the publisher entirely (up to the completion event). compactMap which handles cases where the upstream publisher sends some values that are nil (which is just like compactMap from the Swift Standard Library)
In the next part of the course, you’ll see some additional operators that let you hone in on just the values you need, and if need be, cancel the publisher instead of waiting for all of the values to get emitted.