Vector Algorithms#
Here we’ll look at some of the standard library algorithms that
don’t modify the container. We’ll use std::vector for all
these examples, but other containers in C++ work as well.
Counting repetitions#
We can use std::ranges::count to count how many instances of an element exist in a vector:
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v{1, 1, 4, 6, 10, 1, 3, -2, -8, 0};
auto num = std::ranges::count(v, 1);
std::cout << "the number of times 1 appears in v is " << num << std::endl;
}
Note
If we did this manually, using a loop, we would have to write:
int count{};
for (auto e : v) {
if (e == 1) {
count++;
}
}
so the ranges library version is much more compact.
A variation, std::ranges::count_if takes a function that is used to determine if we count
an element (e.g., only counting odd elements).
Checking if any element matches#
We can use std::ranges::any_of to see if any element in the vector satisfies a condition.
This requires that we pass in a function
the works on a single element and returns a bool.
#include <vector>
#include <algorithm>
#include <iostream>
bool is_even(int e) {
return e % 2 == 0;
}
int main() {
std::vector<int> a{1, 3, 5, 7, 9, 11, 13};
std::vector<int> b{1, 3, 5, 6, 7, 9, 11, 13};
auto test = std::ranges::any_of(a, is_even);
std::cout << "any of a are even? " << test << std::endl;
auto test2 = std::ranges::any_of(b, is_even);
std::cout << "any of b are even? " << test2 << std::endl;
}
There is also an all_of and none_of variant.
Finding an element#
Here’s an example of using find on a vector
(using std::ranges::find):
#include <algorithm>
#include <iostream>
#include <iterator>
#include <ranges>
#include <vector>
int main() {
const std::vector<int> container{100, 200, 300, 400, 500, 600};
// search through the entire vector to find the first instance of the
// element "400"
auto pos = std::ranges::find(container, 400);
if (pos == container.end()) {
std::cout << "element not found" << std::endl;
return 1;
}
std::cout << "element found: " << *pos << std::endl;
// output the remaining elements after the one we searched for
for (int value : std::ranges::subrange(std::next(pos), container.end())) {
std::cout << value << std::endl;
}
}
This returns an iterator that points to the first match in the vector.
Important
Notice the check:
if (pos == container.end()) {
std::cout << "element not found" << std::endl;
return 1;
}
If std::ranges::find does not find a match, then it returns an iterator
that points to one past the end of the vector (that’s what .end() is).
If we want to know the index of the element we found, we could use std::ranges::distance and ask for the distance from the beginning of the vector:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
int main() {
const std::vector<int> container{100, 200, 300, 400, 500, 600};
auto pos = std::ranges::find(container, 400);
if (pos == container.end()) {
std::cout << "element not found" << std::endl;
return 1;
}
// here we seek the distance from the beginning of the vector
auto idx = std::ranges::distance(container.cbegin(), pos);
std::cout << "index = " << idx << std::endl;
// note: we need to be careful here and ensure that idx is >= 0.
// and < the size of the vector. We could be accessing out
// of bounds if the value we searched for was not in the vector
std::cout << "value = " << container[idx] << std::endl;
}
Caution
std::ranges::distance will return a signed integer, since the distance
count be negative, depending on the starting point we used. But we can only
index a vector using an unsigned integer (that is what std::size_t is,
so we need to be careful when we do the indexing here. We know we are safe
because we use .begin() as the starting point.