Github.com/agelessman/…

In a broad sense, there are only three mathematical operations we want for a pipline. To find the maximum, minimum or number in the data set, we can use other Operators for more subtle and precise control.

max

.max() will wait for the upstream publisher to send the.finished event and send the maximum value in the data stream. All problems involving calculating extreme values must implement the Comparable protocol because only this protocol can be used to compare the sizes of two values.

_ = [1.2.3.4]
    .publisher
    .max()
    .sink(receiveCompletion: { completion in
        .
    }, receiveValue: { someValue in
        print("someValue: \(someValue)")})Copy the code

However, in a real development environment, where data flows are likely to be of a variety of types and not all of the data implements the Comparable protocol, what should we do?

For example:

struct Student {
    let age: Int
    let name: String
}

let students = [Student(age: 15, name: "Zhang"),
                Student(age: 16, name: "Bill"),
                Student(age: 17, name: "Fifty")]
Copy the code

Student does not implement the Comparable protocol. Instead, Max provides an additional argument, which is a closure that can be passed in a custom sorting strategy. Its definition is as follows:

public func max(by areInIncreasingOrder: (Publishers.Sequence<Elements.Failure>.Output.Publishers.Sequence<Elements.Failure>.Output) - >Bool) -> Optional<Publishers.Sequence<Elements.Failure>.Output>.Publisher
Copy the code

The point is that areInIncreasingOrder, which requires closure results to adhere to the concept of ascending order, so the code looks like this:

.max { v1, v2 -> Bool in
    v1.age < v2.age
}
Copy the code

The complete code is as follows:

_ = students
    .publisher
    .max { v1, v2 -> Bool in
        v1.age < v2.age
    }
    .sink(receiveCompletion: { completion in
        .
    }, receiveValue: { someValue in
        print("someValue: \(someValue)")})Copy the code

Schematic diagram:

In Combine, any Operator that uses closures is likely to throw an exception, and Max is no exception. For example, if we find a student whose age==0, we throw an exception like this:

enum MyCustomError: Error {
    case customError
}

let students1 = [Student(age: 15, name: "Zhang"),
                Student(age: 16, name: "Bill"),
                Student(age: 0, name: "Fifty")]

_ = students1
    .publisher
    .tryMax { v1, v2 -> Bool in
        if v1.age = = 0 || v2.age = = 0 {
            throw MyCustomError.customError
        }
        return v1.age < v2.age
    }
    .sink(receiveCompletion: { completion in
        print("It's over.")
        switch completion {
        case .finished:
            print("Complete")
        case .failure(let error):
            print("Error:\(error.localizedDescription)")
        }
    }, receiveValue: { someValue in
        print("someValue: \(someValue)")})Copy the code

Once an exception is thrown, the pipline will end and Sink will receive the. Failure event. The schematic diagram is as follows:

min

Min is the opposite of Max, and is used to calculate the minimum value in the data stream, which we won’t explain further. The code is as follows:

.min()
Copy the code
struct Student {
    let age: Int
    let name: String
}

let students = [Student(age: 15, name: "Zhang"),
                Student(age: 16, name: "Bill"),
                Student(age: 17, name: "Fifty")]

.min { v1, v2 -> Bool in
    v1.age < v2.age
}
Copy the code
.tryMin { v1, v2 -> Bool in
    if v1.age = = 0 || v2.age = = 0 {
        throw MyCustomError.customError
    }
    return v1.age < v2.age
}
Copy the code

count

The count is understandable because it waits for the upstream publisher to send the. Finished event and returns the number of data. In some scenarios, this Operator is used when we need to count the number of data so that we don’t need to store the data in an array and count it.

.count()
Copy the code