#
7
Sequence Operators
Written by Shai Mishali

At this point, you know most of the operators that Combine has to offer! How great is that? There’s still one more category for you to dig into, though: **Sequence Operators**.

Sequence operators are easiest to understand when you realize that publishers are just sequences themselves. Sequence operators work with a publisher’s values, much like an array or a set — which, of course, are just finite sequences!

With that in mind, sequence operators mostly deal with the publisher as a whole and not with individual values, as other operator categories do.

Many of the operators in this category have nearly identical names and behaviors as their counterparts in the Swift standard library.

## Getting started

You can find the starter playground for this chapter in **projects/Starter.playground**. Throughout this chapter, you’ll add code to your playground and run it to see how these different sequence operators manipulate your publisher. You’ll use the `print`

operator to log all publishing events.

## Finding values

The first section of this chapter consists of operators that locate specific values the publisher emits based on different criteria. These are similar to the collection methods in the Swift standard library.

`min`

The `min`

operator lets you find the minimum value emitted by a publisher. It’s *greedy*, which means it must wait for the publisher to send a `.finished`

completion event. Once the publisher completes, only the minimum value is emitted by the operator:

Add the following example to your playground to try `min`

:

```
example(of: "min") {
// 1
let publisher = [1, -50, 246, 0].publisher
// 2
publisher
.print("publisher")
.min()
.sink(receiveValue: { print("Lowest value is \($0)") })
.store(in: &subscriptions)
}
```

In this code, you:

- Create a publisher emitting four different numbers.
- Use the
`min`

operator to find the minimum number emitted by the publisher and print that value.

Run your playground and you’ll see the following output in the console:

```
——— Example of: min ———
publisher: receive subscription: ([1, -50, 246, 0])
publisher: request unlimited
publisher: receive value: (1)
publisher: receive value: (-50)
publisher: receive value: (246)
publisher: receive value: (0)
publisher: receive finished
Lowest value is -50
```

As you can see, the publisher emits all its values and finishes, then `min`

finds the minimum and sends it downstream to `sink`

to print it out.

But wait, how does Combine know which of these numbers is the minimum? Well, that’s thanks to the fact numeric values conform to the `Comparable`

protocol. You can use `min()`

directly, without any arguments, on publishers that emit `Comparable`

-conforming types.

But what happens if your values don’t conform to `Comparable`

? Luckily, you can provide your own comparator closure using the `min(by:)`

operator.

Consider the following example, where your publisher emits many pieces of `Data`

and you’d like to find the smallest one.

Add the following code to your playground:

```
example(of: "min non-Comparable") {
// 1
let publisher = ["12345",
"ab",
"hello world"]
.map { Data($0.utf8) } // [Data]
.publisher // Publisher<Data, Never>
// 2
publisher
.print("publisher")
.min(by: { $0.count < $1.count })
.sink(receiveValue: { data in
// 3
let string = String(data: data, encoding: .utf8)!
print("Smallest data is \(string), \(data.count) bytes")
})
.store(in: &subscriptions)
}
```

In the above code:

- You create a publisher that emits three
`Data`

objects created from various strings. - Since
`Data`

doesn’t conform to`Comparable`

, you use the`min(by:)`

operator to find the`Data`

object with the smallest number of bytes. - You convert the smallest
`Data`

object back to a string and print it out.

Run your playground and you’ll see the following in your console:

```
——— Example of: min non-Comparable ———
publisher: receive subscription: ([5 bytes, 2 bytes, 11 bytes])
publisher: request unlimited
publisher: receive value: (5 bytes)
publisher: receive value: (2 bytes)
publisher: receive value: (11 bytes)
publisher: receive finished
Smallest data is ab, 2 bytes
```

Like the previous example, the publisher emits all its `Data`

objects and finishes, then `min(by:)`

finds and emits the data with the smallest byte size and `sink`

prints it out.

`max`

As you’d guess, `max`

works exactly like `min`

, except that it finds the *maximum* value emitted by a publisher:

Add the following code to your playground to try this example:

```
example(of: "max") {
// 1
let publisher = ["A", "F", "Z", "E"].publisher
// 2
publisher
.print("publisher")
.max()
.sink(receiveValue: { print("Highest value is \($0)") })
.store(in: &subscriptions)
}
```

In the following code, you:

- Create a publisher that emits four different letters.
- Use the
`max`

operator to find the letter with the highest value and print it.

Run your playground. You’ll see the following output in your playground:

```
——— Example of: max ———
publisher: receive subscription: (["A", "F", "Z", "E"])
publisher: request unlimited
publisher: receive value: (A)
publisher: receive value: (F)
publisher: receive value: (Z)
publisher: receive value: (E)
publisher: receive finished
Highest value is Z
```

Exactly like `min`

, `max`

is *greedy* and must wait for the upstream publisher to finish emitting its values before it determines the maximum value. In this case, that value is `Z`

.

Note: Exactly like`min`

,`max`

also has a companion`max(by:)`

operator that accepts a predicate to determine the maximum value emitted among non-`Comparable`

values.

`first`

While the `min`

and `max`

operators deal with finding a published value at some unknown index, the rest of the operators in this section deal with finding emitted values at *specific* places, starting with the `first`

operator.

The `first`

operator is similar to Swift’s `first`

property on collections, except that it lets the first emitted value through and then completes. It’s *lazy*, meaning it doesn’t wait for the upstream publisher to finish, but instead will cancel the subscription when it receives the first value emitted.

Add the above example to your playground:

```
example(of: "first") {
// 1
let publisher = ["A", "B", "C"].publisher
// 2
publisher
.print("publisher")
.first()
.sink(receiveValue: { print("First value is \($0)") })
.store(in: &subscriptions)
}
```

In the above code, you:

- Create a publisher emitting three letters.
- Use
`first()`

to let only the first emitted value through and print it out.

Run your playground and take a look at the console:

```
——— Example of: first ———
publisher: receive subscription: (["A", "B", "C"])
publisher: request unlimited
publisher: receive value: (A)
publisher: receive cancel
First value is A
```

As soon as `first()`

gets the publisher’s first value, it cancels the subscription to the upstream publisher.

If you’re looking for more granular control, you can also use `first(where:)`

. Just like its counterpart in the Swift standard library, it will emit the first value that matches a provided predicate — if there is one.

Add the following example to your playground:

```
example(of: "first(where:)") {
// 1
let publisher = ["J", "O", "H", "N"].publisher
// 2
publisher
.print("publisher")
.first(where: { "Hello World".contains($0) })
.sink(receiveValue: { print("First match is \($0)") })
.store(in: &subscriptions)
}
```

In this code, you:

- Create a publisher that emits four letters.
- Use the
`first(where:)`

operator to find the first letter contained in`Hello World`

and then print it out.

Run the playground and you’ll see the following output:

```
——— Example of: first(where:) ———
publisher: receive subscription: (["J", "O", "H", "N"])
publisher: request unlimited
publisher: receive value: (J)
publisher: receive value: (O)
publisher: receive value: (H)
publisher: receive cancel
First match is H
```

In the above example, the operator checks if `Hello World`

contains the emitted letter until it finds the first match: `H`

. Upon finding that much, it cancels the subscription and emits the letter for `sink`

to print out.

`last`

Just as `min`

has an opposite, `max`

, `first`

also has an opposite: `last`

!

`last`

works exactly like `first`

, except it emits the *last* value that the publisher emits. This means it’s also *greedy* and must wait for the upstream publisher to finish:

Add this example to your playground:

```
example(of: "last") {
// 1
let publisher = ["A", "B", "C"].publisher
// 2
publisher
.print("publisher")
.last()
.sink(receiveValue: { print("Last value is \($0)") })
.store(in: &subscriptions)
}
```

In this code, you:

- Create a publisher that will emit three letters and finish.
- Use the
`last`

operator to only emit the last value published and print it out.

Run the playground and you’ll see the following output:

```
——— Example of: last ———
publisher: receive subscription: (["A", "B", "C"])
publisher: request unlimited
publisher: receive value: (A)
publisher: receive value: (B)
publisher: receive value: (C)
publisher: receive finished
Last value is C
```

`last`

waits for the upstream publisher to send a `.finished`

completion event, at which point it sends the last emitted value downstream to be printed out in `sink`

.

Note: Exactly like`first`

,`last`

also has a`last(where:)`

overload, which emits the last value emitted by a publisher that matches a specified predicate.

`output(at:)`

The last two operators in this section don’t have counterparts in the Swift standard library. The `output`

operators will look for a value emitted by the upstream publisher at the specified index.

You’ll start with `output(at:)`

, which emits only the value emitted at the specified index:

Add the following code to your playground to try this example:

```
example(of: "output(at:)") {
// 1
let publisher = ["A", "B", "C"].publisher
// 2
publisher
.print("publisher")
.output(at: 1)
.sink(receiveValue: { print("Value at index 1 is \($0)") })
.store(in: &subscriptions)
}
```

In the above code, you:

- Create a publisher which emits three letters.
- Use
`output(at:)`

to only let through the value emitted at index`1`

— i.e., the second value.

Run the example in your playground and peek at your console:

```
——— Example of: output(at:) ———
publisher: receive subscription: (["A", "B", "C"])
publisher: request unlimited
publisher: receive value: (A)
publisher: request max: (1) (synchronous)
publisher: receive value: (B)
Value at index 1 is B
publisher: receive cancel
```

Here, the output indicates the value at index `1`

is `B`

. However, you might’ve noticed an additional interesting fact: The operator *demands* one more value after every emitted value, since it knows it’s only looking for a single item. While this is an implementation detail of the specific operator, it provides interesting insight into how Apple designs some of their own built-in Combine operators to leverage backpressure.

`output(in:)`

You’ll wrap up this section with the second overload of the `output`

operator: `output(in:)`

.

While `output(at:)`

emits a *single* value emitted at a specified index, `output(in:)`

emits values whose indices are within a provided *range*:

To try this out, add the following example to your playground:

```
example(of: "output(in:)") {
// 1
let publisher = ["A", "B", "C", "D", "E"].publisher
// 2
publisher
.output(in: 1...3)
.sink(receiveCompletion: { print($0) },
receiveValue: { print("Value in range: \($0)") })
.store(in: &subscriptions)
}
```

In the previous code, you:

- Create a publisher that emits five different letters.
- Use the
`output(in:)`

operator to only let through values emitted in indices`1`

through`3`

, then print out those values.

Can you guess what the output of this example will be? Run your playground and find out:

```
——— Example of: output(in:) ———
Value in range: B
Value in range: C
Value in range: D
finished
```

Well, did you guess correctly? The operator emits **individual values** within the range of indices, not a collection of them. The operator prints the values `B`

, `C`

and `D`

as they’re in indices `1`

, `2`

and `3`

, respectively. Then, since all items within the range have been emitted, it cancels the subscription as soon as it receives all values within the provided range.

## Querying the publisher

The following operators also deal with the entire set of values emitted by a publisher, but they don’t produce any specific value that it emits. Instead, these operators emit a different value representing some query on the publisher as a whole. A good example of this is the `count`

operator.

`count`

The `count`

operator will emit a single value - the number of values were emitted by the upstream publisher, once the publisher sends a `.finished`

completion event:

Add the following code to try this example:

```
example(of: "count") {
// 1
let publisher = ["A", "B", "C"].publisher
// 2
publisher
.print("publisher")
.count()
.sink(receiveValue: { print("I have \($0) items") })
.store(in: &subscriptions)
}
```

In the above code, you:

- Create a publisher that emits three letters.
- Use
`count()`

to emit a single value indicating the number of values emitted by the upstream publisher.

Run your playground and check your console. You’ll see the following output:

```
——— Example of: count ———
publisher: receive subscription: (["A", "B", "C"])
publisher: request unlimited
publisher: receive value: (A)
publisher: receive value: (B)
publisher: receive value: (C)
publisher: receive finished
I have 3 items
```

As expected, the value `3`

is only printed out once the upstream publisher sends a `.finished`

completion event.

`contains`

Another useful operator is `contains`

. You’ve probably used its counterpart in the Swift standard library more than once.

The `contains`

operator will emit `true`

and cancel the subscription if the specified value is emitted by the upstream publisher, or `false`

if none of the emitted values are equal to the specified one:

Add the following to your playground to try `contains`

:

```
example(of: "contains") {
// 1
let publisher = ["A", "B", "C", "D", "E"].publisher
let letter = "C"
// 2
publisher
.print("publisher")
.contains(letter)
.sink(receiveValue: { contains in
// 3
print(contains ? "Publisher emitted \(letter)!"
: "Publisher never emitted \(letter)!")
})
.store(in: &subscriptions)
}
```

In the previous code, you:

- Create a publisher emitting five different letters —
`A`

through`E`

— and create a`letter`

value to use with`contains`

. - Use
`contains`

to check if the upstream publisher emitted the value of`letter`

:`C`

. - Print an appropriate message based on whether or not the value was emitted.

Run your playground and check the console:

```
——— Example of: contains ———
publisher: receive subscription: (["A", "B", "C", "D", "E"])
publisher: request unlimited
publisher: receive value: (A)
publisher: receive value: (B)
publisher: receive value: (C)
publisher: receive cancel
Publisher emitted C!
```

Huzzah! You got a message indicating `C`

was emitted by the publisher. You might have also noticed `contains`

is lazy, as it only consumes as many upstream values as it needs to perform its work. Once `C`

is found, it cancels the subscription and doesn’t produce any further values.

Why don’t you try another variation? Replace the following line:

```
let letter = "C"
```

With:

```
let letter = "F"
```

Next, run your playground again. You’ll see the following output:

```
——— Example of: contains ———
publisher: receive subscription: (["A", "B", "C", "D", "E"])
publisher: request unlimited
publisher: receive value: (A)
publisher: receive value: (B)
publisher: receive value: (C)
publisher: receive value: (D)
publisher: receive value: (E)
publisher: receive finished
Publisher never emitted F!
```

In this case, `contains`

waits for the publisher to emit `F`

. However, the publisher finishes without emitting `F`

, so `contains`

emits `false`

and you see the appropriate message printed out.

Finally, sometimes you want to look for a match for a predicate that you provide or check for the existence of an emitted value that doesn’t conform to `Comparable`

. For these specific cases, you have `contains(where:)`

.

Add the following example to your playground:

```
example(of: "contains(where:)") {
// 1
struct Person {
let id: Int
let name: String
}
// 2
let people = [
(123, "Shai Mishali"),
(777, "Marin Todorov"),
(214, "Florent Pillet")
]
.map(Person.init)
.publisher
// 3
people
.contains(where: { $0.id == 800 })
.sink(receiveValue: { contains in
// 4
print(contains ? "Criteria matches!"
: "Couldn't find a match for the criteria")
})
.store(in: &subscriptions)
}
```

The previous code is a bit more complex, but not by much. You:

- Define a
`Person`

struct with an`id`

and a`name`

. - Create a publisher that emits three different instances of
`People`

. - Use
`contains`

to see if the`id`

of any of them is`800`

. - Print an appropriate message based on the emitted result.

Run your playground and you’ll see the following output:

```
——— Example of: contains(where:) ———
Couldn't find a match for the criteria
```

It didn’t find any matches, as expected, because none of the emitted people have an `id`

of `800`

.

Next, change the implementation of `contains(where:)`

:

```
.contains(where: { $0.id == 800 })
```

To the following:

```
.contains(where: { $0.id == 800 || $0.name == "Marin Todorov" })
```

Run the playground again and look at the console:

```
——— Example of: contains(where:) ———
Criteria matches!
```

This time it found a value matching the predicate, since Marin is indeed one of the people in your list. Awesome! :]

`allSatisfy`

A bunch of operators down, and only two to go! Both of them have counterpart collection methods in the Swift standard library.

You’ll start with `allSatisfy`

, which takes a closure predicate and emits a Boolean indicating whether *all* values emitted by the upstream publisher match that predicate. It’s greedy and will, therefore, wait until the upstream publisher emits a `.finished`

completion event:

Add the following example to your playground to try this:

```
example(of: "allSatisfy") {
// 1
let publisher = stride(from: 0, to: 5, by: 2).publisher
// 2
publisher
.print("publisher")
.allSatisfy { $0 % 2 == 0 }
.sink(receiveValue: { allEven in
print(allEven ? "All numbers are even"
: "Something is odd...")
})
.store(in: &subscriptions)
}
```

In the above code, you:

- Create a publisher that emits numbers between
`0`

to`5`

in steps of`2`

(i.e.,`0`

,`2`

and`4`

). - Use
`allSatisfy`

to check if all emitted values are even, then print an appropriate message based on the emitted result.

Run the code and check the console output:

```
——— Example of: allSatisfy ———
publisher: receive subscription: (Sequence)
publisher: request unlimited
publisher: receive value: (0)
publisher: receive value: (2)
publisher: receive value: (4)
publisher: receive finished
All numbers are even
```

Since all values are indeed even, the operator emits `true`

after the upstream publisher sends a `.finished`

completion, and the appropriate message is printed out.

However, if even a single value doesn’t pass the predicate condition, the operator will emit `false`

immediately and will cancel the subscription.

Replace the following line:

```
let publisher = stride(from: 0, to: 5, by: 2).publisher
```

With:

```
let publisher = stride(from: 0, to: 5, by: 1).publisher
```

You simply changed the `stride`

to step between `0`

and `5`

by `1`

, instead of `2`

. Run the playground once again and take a look at the console:

```
——— Example of: allSatisfy ———
publisher: receive subscription: (Sequence)
publisher: request unlimited
publisher: receive value: (0)
publisher: receive value: (1)
publisher: receive cancel
Something is odd...
```

In this case, as soon as `1`

is emitted, the predicate doesn’t pass anymore, so `allSatisfy`

emits `false`

and cancels the subscription.

`reduce`

Well, here we are! The final operator for this rather packed chapter: `reduce`

.

The `reduce`

operator is a bit different from the rest of the operators covered in this chapter. It doesn’t look for a specific value or query the publisher as a whole. Instead, it lets you iteratively accumulate a *new* value based on the emissions of the upstream publisher.

This might sound confusing at first, but you’ll get it in a moment. The easiest way to start is with a diagram:

Combine’s `reduce`

operator works like its counterparts in the Swift standard library: `reduce(_:_)`

and `reduce(into:_:)`

.

It lets you provide a seed value and an accumulator closure. That closure receives the accumulated value — starting with the seed value — and the current value. From that closure, you return *a new* accumulated value. Once the operator receives a `.finished`

completion event, it emits the final accumulated value.

In the case of the above diagram, you can think of it this way :

```
Seed value is 0
Receives 1, 0 + 1 = 1
Receives 3, 1 + 3 = 4
Receives 7, 4 + 7 = 11
Emits 11
```

Time for you to try a quick example to get a better sense of this operator. Add the following to your playground:

```
example(of: "reduce") {
// 1
let publisher = ["Hel", "lo", " ", "Wor", "ld", "!"].publisher
publisher
.print("publisher")
.reduce("") { accumulator, value in
// 2
accumulator + value
}
.sink(receiveValue: { print("Reduced into: \($0)") })
.store(in: &subscriptions)
}
```

In this code, you:

- Create a publisher that emits six
`String`

s. - Use
`reduce`

with a seed of an empty string, appending the emitted values to it to create the final string result.

Run the playground and take a look at the console output:

```
——— Example of: reduce ———
publisher: receive subscription: (["Hel", "lo", " ", "Wor", "ld", "!"])
publisher: request unlimited
publisher: receive value: (Hel)
publisher: receive value: (lo)
publisher: receive value: ( )
publisher: receive value: (Wor)
publisher: receive value: (ld)
publisher: receive value: (!)
publisher: receive finished
Reduced into: Hello World!
```

Notice how the accumulated result — `Hello World!`

— is only printed once the upstream publisher sent a `.finished`

completion event.

The second argument for `reduce`

is a closure that takes two values of some type and returns a value of that same type. In Swift, `+`

is an *also* a function that matches that signature.

So as a final neat trick, you can *reduce* the syntax above. Replace the following code:

```
.reduce("") { accumulator, value in
// 3
return accumulator + value
}
```

With simply:

```
.reduce("", +)
```

If you run your playground again, it will work exactly the same as before, with a bit of a fancier syntax. ;]

Note: Does this operator feel a bit familiar? Well, that might be because you learned about`scan`

in Chapter 3, “Transforming Operators.”`scan`

and`reduce`

have the same functionality, with the main difference being that`scan`

emits the accumulated value foreveryemitted value, while`reduce`

emitsa singleaccumulated value once the upstream publisher sends a`.finished`

completion event. Feel free to change`reduce`

to`scan`

in the above example and try it out for yourself.

## Key points

- Publishers are actually sequences, as they produce values much like collections and sequences do.
- You can use
`min`

and`max`

to emit the minimum or maximum value emitted by a publisher, respectively. -
`first`

,`last`

and`output(at:)`

are useful when you want to find a value emitted at a specific index. Use`output(in:)`

to find values emitted within a*range*of indices. -
`first(where:)`

and`last(where:)`

each take a predicate to determine which values it should let through. - Operators such as
`count`

,`contains`

and`allSatisfy`

don’t emit values emitted by the publisher. Rather, they emit a different value based on the emitted values. -
`contains(where:)`

takes a predicate to determine if the publisher contains the given value. - Use
`reduce`

to accumulate emitted values into a single value.

## Where to go from here?

Congrats on completing the last chapter on operators for this book! give yourself a quick pat on the back and high-five yourself while you’re at it. :]

You’ll wrap up this section by working on your first practical project, where you’ll build a Collage app using Combine and many of the operators you’ve learned. Take a few deep breaths, grab a cup of coffee, and move on to the next chapter.