[Copy from one iterator to another]
[Join container elements into a string]
[Sort containers with std::sort]
[Modify containers with std::transform]
[Find items in a container]
[Generate permutations of data sequences]
[Merge sorted containers]
[Run algorithms in parallel]
[std::sample]
see also:
[Combining two ranges/containers (ranges::views::zip)]
[Algorithms of the range library]
[sort: simplifications with use of ranges]
[iota: an infinite number factory]
STL containers have standardized interfaces. This allows a library of algorithms to operate on all container types sharing the same interface. Compared to handwritten code algorithms are not necessarily shorter, but often they are more efficient and easier to maintain.
From C++20 on the ranges library provides a set of alternative algorithms with simplified interfaces and the possibility to operate with ranges and views.
Copy from one iterator to another
std::vector<std::string> v1 {"one", "two", "three", "four", "five"};
PrintContainer(v1, "v1");
// Copy to target vector of same size
std::vector<std::string> v2(v1.size()); // allocate enough memory for the elements of v1!
std::copy(v1.begin(), v1.end(), v2.begin());
PrintContainer(v2, "v2");
// Insert in empty target vector
std::vector<std::string> v3;
std::copy(v1.begin(), v1.end(), std::back_inserter(v3));
PrintContainer(v3, "v3");
// Use ranges::copy
std::vector<std::string> v4;
std::ranges::copy(v1, std::back_inserter(v4));
PrintContainer(v4, "v4");
// Copy only some of the elements (here: 3 elements starting with the second)
std::vector<std::string> v5;
std::copy_n(v1.begin()+1, 3, std::back_inserter(v5));
PrintContainer(v5, "v5");
// Conditional copy with boolean predicate function
std::vector<std::string> v6;
std::ranges::copy_if(v1, std::back_inserter(v6), [](std::string& s) {return s.size() >= 4; });
PrintContainer(v6, "v6");
// Copy to std::out stream
std::ostream_iterator<std::string> out_it(std::cout, " "); // blank will follow also after last element
std::cout << "Copy to std::out: ";
std::ranges::copy(v1, out_it);
std::cout << '\n';
// Move contents to other vector
std::vector<std::string> v7;
std::ranges::move(v1, std::back_inserter(v7));
PrintContainer(v1, "v1 after move");
PrintContainer(v7, "v7 after move");
Output:
v1: one two three four five
v2: one two three four five
v3: one two three four five
v4: one two three four five
v5: two three four
v6: three four five
Copy to std::out: one two three four five
v1 after move:
v7 after move: one two three four five
Join container elements into a string
When writing several elements into a string proper separators are needed for readability.
As the standard does not fully support this requirement here are some helper functions:
namespace MyUtils
{
// Provide separator between string elements, write result to stream
template<typename I>
std::ostream& join(I it, I end_it, std::ostream& o, std::string_view sep = "")
{
if (it != end_it) o << *it++;
while (it != end_it) o << sep << *it++;
return o;
}
// Provide separator between string elements, write result to string
template<typename I>
std::string join(I it, I end_it, std::string_view sep = "")
{
std::ostringstream o;
join(it, end_it, o, sep);
return o.str();
}
// Overload for container, stream version
template<typename Container>
std::ostream& join(Container& c, std::ostream& o, std::string_view sep = "")
{
return join(std::begin(c), std::end(c), o, sep);
}
// Overload for container, string version
template<typename Container>
std::string join(Container& c, std::string_view sep = "")
{
return join(std::begin(c), std::end(c), sep);
}
}
Example code to format elements:
std::vector<std::string> v {"one", "two", "three", "four", "five"};
// Join words without separators
auto joinedView = std::ranges::views::join(v);
std::cout << "std joined view : ";
for (const char c : joinedView) std::cout << c;
std::cout << '\n';
// Join words with separators using selfwritten utility function
std::cout << "MyUtils joined to std:out: ";
MyUtils::join(v, std::cout, ", ") << '\n';
std::string s = MyUtils::join(v, ", ");
std::cout << "MyUtils joined to string : " << s << '\n';
Output:
std joined view : onetwothreefourfive
MyUtils joined to std:out: one, two, three, four, five
MyUtils joined to string : one, two, three, four, five
Sort containers with std::sort
Helper functions:
#include <random>
void Randomize(auto& c)
{
static std::random_device rd;
static std::default_random_engine rng(rd());
std::shuffle(c.begin(), c.end(), rng);
}
void CheckSorted(auto& c)
{
if (!std::is_sorted(c.begin(), c.end())) std::cout << "un";
std::cout << "sorted: ";
}
void PrintContainerS(const auto& c)
{
CheckSorted(c);
for (auto& e : c) std::cout << e << " ";
std::cout << '\n';
}
Example code to sort elements:
std::vector<int> v {1,2,3,4,5,6,7,8,9,10};
PrintContainerS(v);
Randomize(v);
PrintContainerS(v);
std::ranges::sort(v);
PrintContainerS(v);
// Optionally specify sort algorithm
std::ranges::sort(v, std::greater{});
PrintContainer(v);
std::ranges::sort(v, [](const int& lhs, const int& rhs) {return lhs > rhs; });
PrintContainer(v);
std::cout << "\npartial_sort (sort the first N elements)\n";
Randomize(v);
PrintContainer(v);
// Sort the first 4 elements
std::ranges::partial_sort(v, v.begin() + 4);
PrintContainer(v);
std::cout << "\npartition (move elements with some property to the front)\n";
Randomize(v);
PrintContainer(v);
// Move all elements dividable by 3 to the front
std::ranges::partition(v, [](int& i) {return i % 3 == 0; });
PrintContainer(v);
Output:
sorted: 1 2 3 4 5 6 7 8 9 10
unsorted: 1 8 2 3 6 4 5 10 9 7
sorted: 1 2 3 4 5 6 7 8 9 10
10 9 8 7 6 5 4 3 2 1
10 9 8 7 6 5 4 3 2 1
partial_sort (sort the first N elements
8 9 10 6 3 4 1 2 7 5
1 2 3 4 10 9 8 6 7 5
partition (move elements with some property to the front)
5 3 6 9 4 8 2 10 7 1
9 3 6 5 4 8 2 10 7 1
Modify containers with std::transform
Example code to transform elements:
std::vector<int> v {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::vector<int> v1;
PrintContainer(v);
std::ranges::transform(v, std::back_inserter(v1), [](const int& x) {return x * x; });
PrintContainer(v1);
// Works also with std::string
std::string s{"asdfg"};
std::string s1(s.length(),' '); // ensure same size
PrintContainer(s);
std::ranges::transform(s, s1.begin(), [](const char& c) {return c+1; });
PrintContainer(s1);
std::string s2;
std::ranges::transform(s, std::back_inserter(s2), [](const char& c) {return c + 2; });
PrintContainer(s2);
// Modify the container itself, adjust values using clamp
constexpr int minVal = 3;
constexpr int maxVal = 8;
PrintContainer(v);
std::ranges::transform(v, v.begin(), [=](const int& x) {return std::clamp(x, minVal, maxVal); });
PrintContainer(v);
Output:
1 2 3 4 5 6 7 8 9 10
1 4 9 16 25 36 49 64 81 100
a s d f g
b t e g h
c u f h i
1 2 3 4 5 6 7 8 9 10
3 3 3 4 5 6 7 8 8 8
std::transform does not guarantee that the elements will be processed in order. If required you must explicitly program your own for loop.
Find items in a container
Example code to find elements:
std::vector<int> v {1, 6, 2, 7, 3, 4, 5, 6, 7, 8, 9, 10};
PrintContainer(v);
// Find first element with a given value
auto it7 = std::ranges::find(v, 7);
if (it7 != v.end())
{
std::cout << std::format("Found {} at index {}\n", *it7, std::distance(v.begin(), it7));
}
// Find first element that fulfills a condition
auto it3 = std::ranges::find_if(v, [](const int& i) {return i % 3 == 0; });
if (it3 != v.end())
{
std::cout << std::format("Found {} at index {}\n", *it3, std::distance(v.begin(), it3));
}
// Find all elements that fulfill a condition
auto resultView = std::ranges::views::filter(v, [](const int& i) {return i % 3 == 0; });
if (!resultView.empty())
{
std::cout << std::format("Found following matches: {}\n", MyUtils::join(resultView," "));
}
// OPEN: how can we get all found iterator positions?
Output:
1 6 2 7 3 4 5 6 7 8 9 10
Found 7 at index 3
Found 6 at index 1
Found following matches: 6 3 6 9
If the container element is a general class object, then the class must have an operator==() member function to support ranges::find.
Generate permutations of data sequences
The next_permutation() algorithm generates permutations by re-ordering a container to the next lexicographical permutation:
std::cout << "all permutations, starting with lexicographical sorted container:\n";
std::vector<int> v {1, 2, 3};
do
{
PrintContainer(v);
} while (std::next_permutation(v.begin(), v.end()));
std::cout << "only rest of permutations, starting with lexicographical later sort order:\n";
std::vector<int> v1 {2, 3, 1};
do
{
PrintContainer(v1);
} while (std::next_permutation(v1.begin(), v1.end()));
std::cout << "fewer permutations, same values are not permutated:\n";
std::vector<int> v3 {1, 1, 3};
do
{
PrintContainer(v3);
} while (std::next_permutation(v3.begin(), v3.end()));
Output:
all permutations, starting with lexicographical sorted container:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
only rest of permutations, starting with lexicographical later sort order:
2 3 1
3 1 2
3 2 1
fewer permutations, same values are not permutated:
1 1 3
1 3 1
3 1 1
Merge sorted containers
std::merge takes two sorted sequences and generates a third merged and sorted sequence:
std::vector<std::string> v1 {"bba", "czz", "yxx"}; // is sorted!
std::vector<std::string> v2 {"add", "fhh", "zww"}; // is sorted!
std::vector<std::string> v3;
std::ranges::merge(v1, v2, std::back_inserter(v3));
PrintContainer(v3);
Output:
add bba czz fhh yxx zww
Run algorithms in parallel
Many of the STL algorithms can run in parallel execution with improved performance. Those algorithms accept an execution policy object when invoked.
Example:
#include <execution>
std::sort(std::execution::par_unseq,v.begin(), v.end());
std::transform(std::execution::par, v.begin(), v.end(), v.begin(), [](const int& x) {return x * x; });
For more info on execution policy see https://en.cppreference.com/w/cpp/algorithm/execution_policy_tag_t
std::sample
If you have generated a large set of random data (see std::random_device, std::mt19937, std::normal_distribution,) you can get a smaller set of random data using std::sample, i.e. instead of 200.000 random data you work only with 500 randomly chosen data from the bigger set. For more details see [Bill Weinman, page 194 ff.]