[ad_1]
On this article, we’ll have a look at a brilliant useful ranges view in C++23 – views::zip
. In brief, it means that you can mix two or extra ranges and iterate by way of them concurrently. Let’s see how one can use it.
Fundamental
When you have two (or extra) separate containers and also you need to iterate by way of them “in parallel,” you possibly can write the next code:
std::vector a { 10, 20, 30, 40, 50 };
std::vector<std::string> b { "ten", "twenty", "thirty", "fourty" };
for (size_t i = 0; i < std::min(a.dimension(), b.dimension()); ++i)
std::cout << std::format("{} -> {}n", a[i], b[i]);
We are able to even use iota
to generate indices:
// vary model
for (auto i : std::views::iota(0uz, std::min(a.dimension(), b.dimension())))
std::cout << std::format("{} -> {}n", a[i], b[i]);
As of C++23 you need to use the integer literal suffix for
std::size_t
is any mixture of z or Z with u or U (i.e. zu, zU, Zu, ZU, uz, uZ, Uz, or UZ).
Or extract some names reasonably than utilizing indices:
for (size_t i = 0; i < std::min(a.dimension(), b.dimension()); ++i) {
const auto& num = a[i];
const auto& identify = b[i];
std::cout << std::format("{} -> {}n", num, identify);
}
Play with all examples @Compiler Explorer
As you possibly can see, we are able to simply iterate by way of these containers, however the code will not be elegant. What’s extra, you need to take note of particulars and ensure you use the min
dimension from all collections.
In C++23, we’ve got a nicer answer: zips
!
#embrace <format>
#embrace <iostream>
#embrace <ranges>
#embrace <vector>
int major() {
std::vector a { 10, 20, 30, 40, 50 };
std::vector<std::string> b { "one", "two", "three", "4" };
for (const auto& [num, name] : std::views::zip(a, b))
std::cout << std::format("{} -> {}n", num, identify);
}
How cool is that? And you’ll already play with this in GCC 13 (in addition to partially in Clang 15, and MSVC 17.3).
Suppose you iterate over two ranges zip
yields std::pair
and std::tuple
you probably have extra ranges. My code used structured binding to unpack these tuples into significant names.
This iteration may be particularly useful when your information is break up throughout many containers – like in SOA (Construction of Arrays) reasonably than AOS (Array of structs).
Observe: for having a container with simply indices, you possibly can take a look at views::enumerate (additionally C++23)
However how a couple of extra sensible instance?
Strolling over three containers
Let’s say you’ve gotten three containers:
dates
– dates of the gross salesmerchandise
– what varieties of merchandise had been boughtgross sales
– earnings from every sorts
std::vector<std::string> dates = {
"2023-03-01", "2023-03-01", "2023-03-02", "2023-03-03", "2023-03-03"
};
std::vector<std::string> merchandise = {
"Sneakers", "T-shirts", "Pants", "Sneakers", "T-shirts"
};
std::vector gross sales = {
50.0, 20.0, 30.0, 75.0, 40.0
};
We need to group these information and test what earnings we acquired on a given date, or how a lot we acquired promoting a given class of merchandise.
That’s to zip
we are able to write the next code:
std::map<std::string, double> salesInDay;
std::map<std::string, double> salesPerProduct;
for (const auto & [d, p, s] : std::views::zip(dates, merchandise, gross sales)) {
salesInDay[d] += s;
salesPerProduct[p] += s;
}
for (const auto& [day, sale] : salesInDay)
std::cout << std::format("in {} bought {}n", day, sale);
for (const auto& [prod, sale] : salesPerProduct)
std::cout << std::format("bought {} in {} classn", sale, prod);
The Output:
in 2023-03-01 bought 70
in 2023-03-02 bought 30
in 2023-03-03 bought 115
bought 30 in Pants class
bought 125 in Sneakers class
bought 60 in T-shirts class
Zip rework
However there’s extra. What if we’d wish to compute the earnings primarily based on costs
and prices
:
int major() {
std::vector costs = {100, 200, 150, 180, 130};
std::vector prices = {10, 20, 50, 40, 100};
std::vector<int> earnings;
earnings.reserve(costs.dimension());
for (const auto& [p, c] : std::views::zip(costs, prices))
earnings.emplace_back(p - c); // <<
std::cout << std::accumulate(earnings.start(), earnings.finish(), 0);
}
The code above makes use of zip
, after which shops the computation within the earnings
vector. However because of the associated view: zip_transform
, we are able to write:
int major() {
std::vector costs = {100, 200, 150, 180, 130};
std::vector prices = {10, 20, 50, 40, 100};
std::vector<int> earnings;
earnings.reserve(costs.dimension());
auto compute = [](const auto& p, const auto& c) { return p - c; };
auto v = std::views::zip_transform(compute, costs, prices);
for (const auto& in : v)
earnings.emplace_back(in);
std::cout << std::accumulate(earnings.start(), earnings.finish(), 0);
}
This time, we use a separate view that walks on parts of each containers however then applies the compute
callable object.
Play @Compiler Explorer
Ready for ranges::to
and creating containers
Proper now, you need to use range-based for loops to create different containers, however in hopefully a few weeks, you’ll be capable to write:
std::vector costs = {100, 200, 150, 180, 130};
std::vector prices = {10, 20, 50, 40, 100};
auto compute = [](const auto& p, const auto& c) { return p - c; };
auto v = std::views::zip_transform(compute, costs, prices);
auto earnings = v | std::ranges::to<std::vector>();
Briefly, ranges::to
means that you can pack numerous views and subranges right into a separate container You could course of later.
To unzip a map, test
views::keys
,views::values
or extra genericviews::parts
. See at cppreference.com.
Abstract
On this quick article, we checked out a useful view known as std::views::zip
. It means that you can “stroll” on parts of a number of containers directly.
Earlier than C++20/23, you may be conversant in enhance mix or ranges v3 that allowed for such iteration method. However for instance, enhance mix wasn’t ready for structured binding.
Should you marvel why it’s higher to make use of
views::abc
reasonably thanranges::abc_view
see this associated article by Barry R.: Choose views::meow | Barry’s C++ Weblog
Again to you
Do you employ zip
views in your code? Possibly you used some customized code or the enhance strategy?
Be a part of the dialogue under, or at reddit/cpp.
[ad_2]