How to access or modify every n-th element of a range (Range-v3)
In the book Fully Functional C++ with Range-v3, we demonstrated how to access every n-th element of a range. This article additionally presents how to modify every n-th element of a range. Note that many convenience views that are used in this article are currently only available in Range-v3 (not in C++20).
How to access every n-th element of a range
We will firstly repeat how to access every n-th element. Range-v3 offers views::stride(n) for that purpose. For example, to access every second or every third element, we do the following:
#include <iostream> #include <range/v3/all.hpp> using namespace ranges; int main() { auto rng = views::iota(0); // [0,1,2,3...] auto r_sec = rng | views::stride(2); // [0,2,4,6....] auto r_third = rng | views::stride(3); // [0,3,6,9...] }To offset the beginning, e.g. to start at the first index (as opposed to index 0), we additionally use views::drop.
#include <iostream> #include <range/v3/all.hpp> int main() { auto rng = views::iota(0); // [0,1,2,3...] auto r_sec = rng | views::drop(1) | views::stride(2); // [1,3,5,7...] auto r_third = rng | views::drop(1) | views::stride(3); // [1,4,7,10...] }
How to modify every n-th element of a range
To modify every n-th element of a range we have to abandon the drop/stride tactics and introduce views::zip and views::cycle. The idea goes as follows: We generate a range that identifies positions of the elements in the original range. Nevertheless, these positions aren't absolute, i.e. not 0,1,2,3,4,5... but relative. For example, if we are interested in every 3rd element, we generate a range of relative positions 0,1,2,0,1,2... . This can be achieved with the help of views::cycle.
Once we have the cyclic range, we pair it (zip it) with the original range. Now we have a way of identifying elements that need to be replaced. Finally, we use views::transform to transform the pairs of interest back to single elements.
The problem is demonstrated below. There we wish to change every third letter of alphabet to the letter 'x'. The last line instructs that we return 'x' anytime the cyclic range attains the value 2, otherwise we return the original value of the source range.
#include <iostream> #include <range/v3/all.hpp> using namespace ranges; int main() { auto n = 3; // position of an element that should be modified auto rng = views::closed_iota('a','z'); // [a,b,c,d...z] auto r_cyc = views::iota(0,n) | views::cycle; // [0,1,2,0,1,2...] auto r_zipped = views::zip(rng,r_cyc); // (a:0)(b:1)(c:2)(d:0)(e:1){f:2)... auto r_repl = r_zipped | views::transform([n](auto const& p){return (p.second == n - 1) ? 'x' : p.first;}); // [a,b,x,d,e,x...z] }
Intrigued?
Get startedTagged: Range-v3