liveBook · Manning

Filtering

As explained in 100 C++ Mistakes and How to Avoid Them

Filtering is a data-processing operation that retains or rejects each input element according to a predicate.

Filtering retains or rejects each input element using a predicate. A true result retains the element; a false result eliminates it. In ranges, filtering becomes one stage in a composable pipeline. section 4.4

Filtering after transformation

A ranges pipeline first transforms values to their absolute values, then filters out odd results. The remaining even values can be passed to accumulation without a separate intermediate vector. section 4.4

A practical maintenance mistake is hiding different filtering behaviors behind a Boolean flag. A call such as cleanse(values, false) forces readers to inspect the implementation before they know whether it selects values above or below zero. section 11.10

Prefer filtering code that makes its behavior obvious:

  • Give distinct behaviors distinct names, such as filter_above and filter_below. section 11.10
  • Accept some duplication when combining similar operations would spread hidden knowledge or make calls harder to understand. section 11.10
  • Use ranges and lambdas when a sequence naturally forms a pipeline of transformations, filters, and reductions. section 4.4
Listing 4.9 Composed functions on a value sequence
int main() {
    std::vector<int> vals { 42, 3, 7 -9, 0, 22, 23, -7, 22 };
 
    auto evens = vals
        | std::views::transform([](int x) { return abs(x); })
        | std::views::filter([](int x){ return x % 2 == 0; }); #1
    auto sum = std::accumulate(evens.begin(), evens.end(), 0); #2
 
    std::cout << sum << '\n';
    return 0;
}

Inspect a complete C++ example that composes absolute-value mapping, even-value filtering, and accumulation. section 4.4

Listing 11.17 Filtering data both above and below zero in one function
std::vector<double> cleanse(const std::vector<double>& values, 
        bool less_than) {
    std::vector<double> new_values;
    for (int i = 0; i < values.size(); ++i)
        if (less_than) {                                    #1
            if (values[i] < 0)
                new_values.push_back(values[i]);
        } else
            if (values[i] > 0)
                new_values.push_back(values[i]);
    return new_values;
}
 
int main() {
    std::vector<double> values;
    values.push_back(3.14159);
    values.push_back(-1.23456);
    values.push_back(2.71828);
    values.push_back(-3.14159);
 
    std::vector<double> above = cleanse(values, false);     #2
    for (int i = 0; i < above.size(); ++i)
        std::cout << above[i] << ' ';
    std::cout << '\n';
    
    std::vector<double> below = cleanse(values, true);      #3
    for (int i = 0; i < below.size(); ++i)
        std::cout << below[i] << ' ';
    std::cout << '\n';
    return 0;
}

Compare the flag-controlled cleansing function with clearer alternatives when maintaining legacy filtering code. section 11.10

How 100 C++ Mistakes and How to Avoid Them covers it

How deep it goes
Defines predicate-based retention, demonstrates composition, and addresses naming and responsibility but stops short of deeper implementation details.
Examples
Demonstrates even-value selection, absolute-value transformation, accumulation, and filtering positive or negative numbers.
What it assumes
Assumes basic C++ containers, loops, functions, Boolean results, and lambdas while defining predicates and pipeline roles directly.
What you can do after
Apply filtering through predicates, composable ranges pipelines, or clearly named functions for legacy data-cleaning code.
How it connects
Connects filtering with predicates, ranges, lambdas, mapping, reduction, pipelines, and data cleansing.

Also explained in

Compare how 33 books explain filtering →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 C++ Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.