How can I use multiple containers in a range-based for? - c++

I've got multiple containers of different types.
I'd like to perform the same action on all of the elements across all the containers.
Normally, this involves multiple range-based for loops with duplicated code:
#include <iostream>
#include <vector>
#include <set>
int main() {
//containers
std::vector<int> v1{1,2,3,4,5};
std::set<float> v2{6,7,8,9,10};
//perform iterations
for (auto & it: v1){
std::cout << it << ' ';
}
for (auto & it: v2){
std::cout << it << ' ';
}
}
I'd like to be able to do this instead,
by providing multiple containers to the same range-based for loop.
This of course does not work:
for (auto & it: v1,v2){
std::cout << it << ' ';
}
Is there a library solution I could use to achieve this?

You could use boost range's combine:
for(auto&& el : boost::combine(v1, v2)) {
std::cout << boost::get<0>(el) << ", " << boost::get<1>(el) << '\n';
}
demo
Or, range-v3's zip view:
for(auto&& el : view::zip(v1, v2)) {
std::cout << std::get<0>(el) << ", " << std::get<1>(el) << '\n';
}
demo
Alternatively, you could make a range from zip iterators the hard way:
auto b = boost::make_zip_iterator(boost::make_tuple(v1.begin(), v2.begin()));
auto e = boost::make_zip_iterator(boost::make_tuple(v1.end(), v2.end()));
for(auto&& tup : boost::make_iterator_range(b, e)) {
std::cout << boost::get<0>(tup) << ", " << boost::get<1>(tup) << '\n';
}
demo

Related

Two instances of keyword auto in cpp

First one being:
map <int,int> m;
//... some elements inserted
auto i= m.begin();
cout<<(*i).first<<(*i).second;
Here we are required to use the dereference operator *
Second:
map <int,int> m;
//... some elements inserted
for(auto i: m)
cout<<i.first<<i.second;
Why am I not required to use the * operator this time?
One more doubt:
for(auto &i: m)
what difference does '&' make here?
auto i=m.begin() will give you iterator .. which is accessed more like a pointer (syntactically) when you want to access the value...
for(auto i:m) will copy current element of m (a pair) into i , i is a copy of element, not the element itself...
for (auto &i: m) will work on a reference, the original map is affected
As explained in the below code snippet, the first i is of type iterator where as the i in the for loops are of pair type.
#include <iostream>
#include <map>
int main()
{
std::map <int,int> m;
m[1] = 5;
m[10] = 60;
m[100] = 800;
// Below i is map iterator (std::map<int, int>::iterator)
auto i = m.begin();
std::cout << typeid(i).name() << '\n';
std::cout << (*i).first << " : " << (*i).second << '\n';
std::cout << i->first << " : " << i->second << '\n';
for(auto i: m) {
// Below i is pair<int, int>
std::cout << typeid(i).name() << '\n';
std::cout << i.first << " : " << i.second << '\n';
}
for(auto& i: m) {
// Below i is reference of pair<int, int>)
// modifying this would result in updated values in the map.
std::cout << typeid(i).name() << '\n';
std::cout << i.first << " : " << i.second << '\n';
}
return 0;
}

How can I print the elements of a vector as a ordered list using a range-based loop

Ex:
vector<string> myVec = {apple, banana, grape}
How can I print these elements as an ordered list using a range-based loop
Output:
1 apple
2 banana
3 grape
A variation of Jeffrey's answer, but without additional variable:
for (const auto& s : myVec)
{
std::cout << &s - &myVec[0] << " " << s << "\n";
}
This, of course, prints a "correct" 0-based index. Feel free to add 1 to it :)
Using boost ranges really simplifies things Live Demo
using namespace boost::adaptors;
std::vector<std::string> myVec = {"apple", "banana", "grape"};
for (const auto& element : myVec | indexed(1))
{
std::cout << element.index() << " " << element.value() << "\n";
}
Produces
1 apple
2 banana
3 grape
You are looking for
size_t position = 1;
for(const auto& s: myVec)
{
std::cout << position << " " << s << "\n";
position++;
}
as in
#include <iostream>
#include <string>
#include <vector>
using std::vector;
using std::string;
vector<string> myVec = {"apple", "banana", "grape"};
int main()
{
size_t position = 1;
for(const auto& s: myVec)
{
std::cout << position << " " << s << "\n";
position++;
}
}
With range-v3, you could write:
for (auto [i, val] : myVec | ranges::views::enumerate)
{
std::cout << i << ' ' << val << "\n";
}
Here's a demo.
This would be a good issue for the original for loop:
const size_t quantity = myVec.size();
for (unsigned int i = 0; i < quantity; ++i)
{
cout << (i + 1) << " " << myVec[i] << "\n";
}
Simple, effective. Don't knock the old stuff. :-)

How to insert and iterate elements to this kind of map. C++

I tried on my own. But I was unable to do it. So, please help.
unordered_map<string, pair<string , vector<int>>> umap;
Or more precisely, how We can make pair of one string and one vector that can be used in map.
Well you can use insert function and insert them as a pair (Or precisely nested pairs).
For example Checkout this program :
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
using namespace std;
int main()
{
unordered_map<string, pair<string , vector<int>>> umap;
//Insert like this
umap.insert(make_pair("first", make_pair("data1",vector<int>(3, 0))));
umap.insert(make_pair("second", make_pair("data2",vector<int>(3, 1))));
//or like this
string s = "new", t= "method";
vector <int> v = {1, 2, 3};
umap.insert(make_pair(s, make_pair(t, v)));
//This will print all elements of umap
for(auto p : umap)
{
cout << p.first << " -> " << p.second.first << " , VECTOR : " ;
for(auto x : p.second.second)
cout << x << ',';
cout << endl;
}
cout << endl;
//Let's see how to change vector already inside map
auto itr = umap.begin();
cout << "BEFORE : ";
for(auto x : (itr->second).second)
{
cout << x << ',';
}
cout << endl;
//We will push 42 here
(itr->second).second.push_back(42);
cout << "AFTER : ";
for(auto x : (itr->second).second)
{
cout << x << ',';
}
cout << endl;
}
Output Is :
new -> method , VECTOR : 1,2,3,
second -> data2 , VECTOR : 1,1,1,
first -> data1 , VECTOR : 0,0,0,
BEFORE : 1,2,3,
AFTER : 1,2,3,42,
I hope this helps.
This depends a lot on the complexity of what you are creating. For example, if you have some constants in your vector, you could make them in place:
umap.emplace("s", std::make_pair("s2", std::vector<int>{1, 2, 3, 4}));
It is more likely however that you will be making the internals in some complex way. In which case you could more easily do it as separate constructions.
std::vector<int> values;
values.push_back(1);
auto my_value = std::make_pair("s2", std::move(values));
umap.emplace("s2", std::move(my_value));
Using move to move the data around ensures minimal copying.
Finally, to iterate the items, it is normal to use range-based for loops:
for (const auto& [key, value]: umap) {
std::cout << key << ": ";
const auto& [name, values] = value;
std::cout << name << " {";
for (auto val : values) {
std::cout << val << " ";
}
std::cout << "}\n";
}
Here you can check out a live example.

How to print the content of a nested std::unordered_map?

I'm trying to print all the content of an std::unordered_map specified like this:
std::unordered_map<uint64_t, std::unordered_map<uint64_t,uint64_t>> m;
After adding things in the map, I tried the following:
for (auto it=map.begin(); it!=map.end(); it++) {
cout << it->first << it->second << endl;
}
but it is not working.
Since you have nested std::unordered_map, following should work:
for (auto const& i : m) {
for (auto const& j : i.second) {
std::cout << j.first << " " << j.second << std::endl;
}
}
You have to iterate over the nested map as well. And when you work with maps it is very convenient to use a range-based for on top of a structured binding. To avoid these cryptic first and second things:
for (const auto& [key1, value1] : map)
for (const auto& [key2, value2] : value1)
std::cout << key2 << " " << value2 << std::endl;
It works only in C++17 though. If you cannot use it, then you have the answer by NutCracker.
How to print the content of a nested std::unordered_map?
To print nested std::unordered_map use nested range-based for loop.
for (auto const& i: m) {
std::cout << "Key: " << i.first << " (";
for (auto const& j: i.second)
std::cout << j.first << " " << j.second;
std::cout << " )" << std::endl;
}
However, if you want to modify the container's elements:
for (const& i: m) {
for (const& j: i.second)
// Do operations
}

Replace BOOST_FOREACH with "pure" C++11 alternative?

Is it possible to replace the BOOST_FOREACH in this example with a "pure" C++11 equivalent?
#include <map>
#include <functional>
#include <boost/foreach.hpp>
#include <iostream>
int main() {
std::map<int, std::string> map = {std::make_pair(1,"one"), std::make_pair(2,"two")};
int k;
std::string v;
BOOST_FOREACH(std::tie(k, v), map) {
std::cout << "k=" << k << " - " << v << std::endl;
}
}
The key feature being keeping the key/value pair in the references to k and v.
I tried:
for(std::tie(k,v) : map)
{
std::cout << "k=" << k << " - " << v << std::endl;
}
and
auto i = std::tie(k,v);
for(i : map)
{
std::cout << "k=" << k << " - " << v << std::endl;
}
But none of the ranged based for loop ideas seemed to work. Presumably the ranged based for loop is required to have a declaration before the :, since even:
std::vector<int> test;
int i;
for (i : test);
Isn't valid.
The closest equivalent I can find is:
for (auto it = map.begin(); it!=map.end() && (std::tie(k,v)=*it,1); ++it)
{
std::cout << "k=" << k << " - " << v << std::endl;
}
which isn't quite as succinct as the BOOST_FOREACH version!
Is there a way to express the same thing succinctly without boost in C++11?
for (auto & i : map)
{
std::tie(k,v) = i;
// your code here
}
This produces the same output as the Boost macro
for( auto const& k : map ) {
std::cout << "k = " << k.first << " - " << k.second << std::endl;
}
With C++17 this can now be done using structured bindings, for instance:
#include <map>
#include <string>
#include <iostream>
int main() {
const std::map<std::string, std::string> map = {std::make_pair("hello", "world")};
for (auto& [k,v]: map) {
std::cout << "k=" << k << ", v=" << v << "\n";
}
}
This is certainly what I'd choose to do in newer projects.