Algorithms and Ranges

Algorithms and Ranges

Searching, sorting, transforming, and filtering with the STL and modern ranges.

Algorithms and Ranges

Common algorithms

std::sort(values.begin(), values.end());
auto it = std::find(values.begin(), values.end(), 42);
int count = std::count(values.begin(), values.end(), 0);
std::reverse(values.begin(), values.end());

Transform and copy

std::vector<int> doubled(values.size());
std::transform(values.begin(), values.end(), doubled.begin(),
               [](int x) { return x * 2; });

Predicates

bool any_negative = std::any_of(values.begin(), values.end(),
                                [](int x) { return x < 0; });

C++20 ranges

namespace views = std::views;
auto even_squares = values
    | views::filter([](int x) { return x % 2 == 0; })
    | views::transform([](int x) { return x * x; });

Important ideas

Best-practice reminders

Common ranges algorithms

auto pos = std::ranges::find(values, 42);
std::ranges::sort(values);
auto sum = std::accumulate(values.begin(), values.end(), 0);

Projections

struct User { std::string name; int score; };

std::ranges::sort(users, std::greater<>{}, &User::score);

Lazy views vs materialized results

auto active_names = users
    | std::views::filter([](const User& user) { return user.score > 0; })
    | std::views::transform([](const User& user) { return user.name; });

Quick decision guide

Common composition pattern

struct User {
    std::string name;
    int score;
};

auto top_names = users
    | std::views::filter([](const User& user) { return user.score >= 90; })
    | std::views::transform([](const User& user) { return user.name; });

That shape is the common ranges pipeline: filter first, project second, and only materialize if the calling code needs owned strings or repeated traversal.

Materialize-at-boundary habit

std::vector<std::string> names;
for (const auto& name : top_names) {
    names.push_back(name);
}

Keep the lazy pipeline inside one stage of the program, then materialize right before you cross into storage, caching, or an API that expects owned values.

Example in practice

#include <algorithm>
#include <ranges>
#include <vector>

int main() {
    std::vector<int> values{3, 1, 2, 4};
    auto even = values | std::views::filter([](int value) { return value % 2 == 0; });
    return std::ranges::find(even, 4) != even.end() ? 0 : 1;
}

Try this variation

Replace the hand-written search loop with a ranges pipeline. It highlights when composition improves clarity and when it becomes too indirect.

#include <ranges>
#include <vector>

int main() {
    std::vector<int> values{1, 2, 3, 4, 5, 6};
    auto odd = values | std::views::filter([](int value) { return value % 2 == 1; });
    return *odd.begin();
}