#include <iostream>
#include <map>
using namespace std;
int main() {
map<int, int> ma;
// 원소를 추가 하자!!
ma.insert(make_pair(100, 2)); // key 값 : 1 , value : 3
ma.insert(make_pair(101, 3)); // key 값 : 3, value : 13
ma.insert(make_pair(102, 2)); // key 값 : 3, value : 13
ma.insert(make_pair(103, 3)); // key 값 : 3, value : 13
ma.insert(make_pair(104, 1)); // key 값 : 3, value : 13
// make_pair 형식으로 저장 했으므로
// key 값은 fisrt 로 접근 value 값은 second 로 접근한다.
for (auto iter : ma) {
cout << "key : " << iter.first << " value : " << iter.second << '\n';
iter.second = iter.second + 1;
}
cout << "\n" << "\n";
for (auto iter : ma) {
cout << "key : " << iter.first << " value : " << iter.second << '\n';
}
cout << "\n" << "\n";
return 0;
}
Actually , I want to change the value of second component of pair.
iter.second = iter.second + 1;
This code can't change the thing...
How can I change ???
The auto keyword in C++ always tries to infer a non-reference type, so in your case it infers iter as being type std::pair<const int, int>. This is not a reference type, so the values from the map get copied into the iter variable, and so any changes made to iter are not reflected in the map itself.
You can write auto& instead to force an lvalue-reference type:
for (auto& iter : ma) { // <-- Notice ampersand here
iter.second = iter.second + 1;
}
Your main problem is that you called the variable "iter" so you must think it is an iterator of some sort. If it were an iterator, your code would work fine:
#include <iostream>
#include <map>
int main() {
std::map<int, int> map {
{100, 2}, {101, 3}, {102, 2}, {103, 3}, {104, 1},
};
for (auto iter = map.begin(), end = map.end(); iter != end; ++iter) {
std::cout << "key : " << iter->first << " value : " << iter->second << '\n';
++(iter->second);
}
std::cout << "\n\n";
for (auto iter = map.begin(), end = map.end(); iter != end; ++iter) {
std::cout << "key : " << iter->first << " value : " << iter->second << '\n';
}
std::cout << "\n\n";
}
(See online)
But since you are using a range-based for loop you are accessing the values themselves, not an iterator to them. In that case, if you use auto you are getting a copy of the elements, and any changes you make are done the copies, not the elements themselves. If you instead use auto& you will get a reference instead, which is what you want:
#include <iostream>
#include <map>
int main() {
std::map<int, int> map {
{100, 2}, {101, 3}, {102, 2}, {103, 3}, {104, 1},
};
for (auto& [key, val] : map) {
std::cout << "key : " << key << " value : " << val << '\n';
++val;
}
std::cout << "\n\n";
for (auto& [key, val] : map) {
std::cout << "key : " << key << " value : " << val << '\n';
}
std::cout << "\n\n";
}
(See online)
Do note the use of structured bindings to simplify the code.
Related
How could I detect the last iteration of a map using structured bindings?
Here's a concrete example: I have the following simple code whereby I print elements from an std::map using structured bindings from C++17:
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<std::string, size_t> mymap {{"a", 0}, {"b", 1}, {"c", 2}, {"d", 3}};
// using structured bindings, C++17
for (auto const& [key, value] : mymap) {
std::cout << "key: " << key << ", value: " << value << ", ";
}
return 0;
}
The problem is, this will result in a trailing comma, i.e.
key: a, value: 0, key: b, value: 1, key: c, value: 2, key: d, value: 3,
Inspired by this question: How can I detect the last iteration in a loop over std::map?, I can write code with an iterator to print out the std::map contents without the trailing comma, i.e.
for (auto iter = mymap.begin(); iter != mymap.end(); ++iter){
// detect final element
auto last_iteration = (--mymap.end());
if (iter==last_iteration) {
std::cout << "\"" << iter->first << "\": " << iter->second;
} else {
std::cout << "\"" << iter->first << "\": " << iter->second << ", ";
}
}
How does one do this with for (auto const& [key, value] : mymap)? If I know the final key in std::map, I could write an conditional for it; but is there another way without resorting to iter?
Yakk's answer inspired me to write an iterators_of using Ranges and without introducing a new class:
#include <iostream>
#include <map>
#include <string>
#include <ranges>
template<class Range>
auto iterators_of(Range&& r){
return std::ranges::views::iota(std::begin(r), std::end(r));
}
int main() {
std::map<std::string, size_t> mymap {{"a", 0}, {"b", 1}, {"c", 2}, {"d", 3}};
for(auto it : iterators_of(mymap) ) {
auto const& [key, value] = *it;
if(std::next(it) != mymap.end()) {std::cout << "key: " << key << ", value: " << value << ", ";}
else {std::cout << "key: " << key << ", value: " << value;}
}
return 0;
}
https://godbolt.org/z/rx15eMnWh
When I need location as well as data, I write an iterators_of adapter that takes a range, and returns a range of its iterators.
for( auto it:iterators_of(foo) ){
auto const&[key,value]=*it;
// blah
if (std:next(it)!=foo.end())
std::cout<<',';
}
iterators of is short.
template<class T>
struct index{
T t;
T operator*()const{return t;}
index& operator++(){++t; return *this;}
index operator++(int)&{auto self=*this; ++*this; return self;}
bool operator==(index const&)=default;
auto operator<=>(index const&)=default;
};
then augment that to be a full iterator or just write
template<class It, class Sent=It>
struct range{
It b;
Sent e;
It begin()const{return b;}
Sent end()const{return e;}
};
template<class R>
auto iterators_of(R&& r){
using std::begin; using std::end;
return range{index{begin(r)},index{end(r)}};
}
which isn't much code.
I find this is better than manually futzing with iterator iteration.
Short answer, you can't. (That is not what range loops are for.)
What you can do is divide the original range in two subranges:
for(auto const& [key, value] : std::ranges::subrange(mymap.begin(), std::prev(mymap.end())) ) {
std::cout << "key: " << key << ", value: " << value << ", ";
}
for(auto const& [key, value] : std::ranges::subrange(std::prev(mymap.end()), mymap.end()) ) {
std::cout << "key: " << key << ", value: " << value;
}
https://godbolt.org/z/4EWYjMrqG
or a bit more functional:
for(auto const& [key, value] : std::ranges::views::take(mymap, mymap.size() - 1)) {
std::cout << "key: " << key << ", value: " << value << ", ";
}
for(auto const& [key, value] : std::ranges::views::take(std::ranges::views::reverse(mymap), 1)) {
std::cout << "key: " << key << ", value: " << value;
}
or you can "index" the elements of the range. (STD Ranges still lacks zip to do this.)
#include <iostream>
#include <map>
#include <string>
#include <boost/range/adaptor/indexed.hpp>
int main() {
std::map<std::string, size_t> mymap {{"a", 0}, {"b", 1}, {"c", 2}, {"d", 3}};
for(auto const& [index, kvp] : mymap | boost::adaptors::indexed(0) ) {
auto const& [key, value] = kvp;
if(index != mymap.size() - 1) {std::cout << "key: " << key << ", value: " << value << ", ";}
else {std::cout << "key: " << key << ", value: " << value;}
}
return 0;
}
https://godbolt.org/z/f74Pj1Gsz
(Amazing that Boost.Ranges works with structured bindings as if it were brand new.)
The whole point is, as you see, you are fighting against the language by forcing yourself to use a range-for loop.
It looks more attractive to use the iterator based loop.
From C++20 onward you can use an init-statement and write it like this:
#include <iostream>
#include <map>
#include <string>
int main()
{
std::map<std::size_t, std::string> map{ {11, "a"}, {13, "b"}, {22, "c"}, {32, "d"} };
std::size_t end = map.size() - 1;
for (std::size_t n{ 0 }; auto const& [key, value] : map)
{
std::cout << "key: " << key << ", value: " << value;
if ((n++) != end) std::cout << ", ";
}
return 0;
}
After having already seen three possible solutions, here a fourth (and admittedly rather simple)…
Thereby, I moved the separator to the begin of the output and take care that it's modified before the second iteration:
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<std::string, size_t> mymap {{"a", 0}, {"b", 1}, {"c", 2}, {"d", 3}};
// using structured bindings, C++17
const char* sep = "";
for ( auto const& [key, value] : mymap) {
std::cout << sep << "key: " << key << ", value: " << value;
sep = ", ";
}
return 0;
}
Output:
key: a, value: 0, key: b, value: 1, key: c, value: 2, key: d, value: 3
Demo on coliru
What about printing , in the beginning for all key and value pairs except the first one.
IF first_key_value_pair:
print(key, value)
ELSE:
print(',' + (key, value))
We can use a boolean to take care of the first key-value pair.
std::map<char, int> m = {{'a', 1}, {'b', 1}, {'c', 1}, {'d', '1'}};
bool first = true;
for(const auto& [key, value] : m){
if(first) first = false; else std::cout << ", ";
std::cout << "Key: " << key << ", Value: " << value;
}
Output: Key: a, Value: 1, Key: b, Value: 1, Key: c, Value: 1, Key: d, Value: 49
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;
}
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.
I have tried below code snippets to print map of map values but I could not able to access second map values.
#include <iostream>
#include <iterator>
#include <map>
#include <string>
using namespace std;
int main()
{
map< string, std::map<std::string, int> > someStorage;
//First key values
someStorage["key1"]["This Is Layer one"] = 100;
someStorage["Key1"]["This Is Layer Two"] = 120;
//second key, values
someStorage["key2"]["This Is Layer one"] = 110;
someStorage["key2"]["This Is Layer Two"] = 110;
map< string, std::map<std::string, int> > ::iterator itr;
cout << "\nThe map is : \n";
for (itr = someStorage.begin(); itr != someStorage.end(); ++itr)
{
cout << '\t' << itr->first;
//<< '\t' << (itr->second).first << '\n' <==problematic part
//<< '\t' << (itr->second).second << '\n'; <==problematic part
}
cout << endl;
return 0;
}
How to print/access these values and how do I differentiate "This Is Layer
one" for "key1" and "key2". Because I can see that it is getting
overwritten if we assign key2 value, key1 has same. Why?
Also I am expecting below key value pairs
Key1 => {This Is Layer one, 100}
{This Is Layer Two, 120}
Key2 =>{This Is Layer one, 110}
{This Is Layer Two, 110}
.
In addition to the other answers here, you can use structured binding (since c++17) to simplify this:
for (auto const& [key, val] : someStorage) { // val = second map
for (auto const& [k, v] : val) { // k = first, v = second
cout << key << ' ' << k << ' ' << v << '\n';
}
}
You need a second, inner loop to traverse the nested std::map. Like this:
for (auto itr = someStorage.cbegin(); itr != someStorage.cend(); ++itr)
{
cout << itr->first << " => ";
for (auto innerItr = itr->second.cbegin(); innerItr != itr->second.cend(); ++innerItr)
{
cout << innerItr->first << " : " << innerItr->second << " ";
}
cout << "\n";
}
Note that for the desired output, you need to capitalize the keys such that they are "Key1" and "Key2" (this is currently a typo in your question). Note further that I changed to begin/end member functions to cbegin/cend, as the loop doesn't modify the container.
You'll need to iterate over your inner map as well, something like:
for (auto itr1 = someStorage.begin(); itr1 != someStorage.end(); ++itr1)
{
cout << '\t' << itr->first << ":\n";
for (auto itr2 = itr1->second.begin(); itr2 != itr1->second.end(); ++itr2)
{
cout << "\t\t" << itr2->first << '\n';
cout << "\t\t" << itr2->second << '\n';
}
}
Thank you for the output. My Gcc version did not support auto iterate
for (itr1 = someStorage.begin(); itr1 != someStorage.end(); ++itr1)
{
cout << '\t' << itr1->first << ":\n";
std::map<std::string, int> ::iterator itr2;
for (itr2 = itr1->second.begin(); itr2 != itr1->second.end(); ++itr2)
{
cout << "\t\t" << itr2->first << '\n';
cout << "\t\t" << itr2->second << '\n';
}
}
I am having a map like this
typedef vector< pair<int, string> > Categories;
map<string,Categories> cats;
but when I am trying to read elements of the map like
for(map<string,Categories>::const_iterator it = cats.begin(); it != cats.end(); ++it)
{
std::cout << it->first << " " << it->second.first<<"\n";
}
I do get errors
error: 'const class std::vector<std::pair<int, std::basic_string<char>
' has no member named 'first' std::cout << it->first << " " << it-> second.first<<"\n";
error: 'const class std::vector ' has no member named 'first'
std::cout << it->first << " " << it->second.first<<"\n";
Its clear as Crystal, that you might have many elements in your values of your map, which is a std::vector< std::pair<int, std::string>>
Then how would you print elements of a vector? The options are:
random access (i.e, vec[index])
iterator (i.e, std::vector< std::pair<int, std::string>>::const_iterator itr;)
or by a range based for loop (i.e, for(const auto& it: vec) )
In your case, if you wanna have something simple and easy code is using a range based loop:
for(const auto& it: cats)
{
std::cout << it.first << " = "; // keys
for(const auto& ve: it.second) // values vec
std::cout << ve.first << "-" << ve.second << "\t";
std::cout << std::endl;
}
If you still wanna have long iterator loops, here is it.
see live action here: https://www.ideone.com/3bS1kR
#include <string>
#include <iostream>
#include <vector>
#include <map>
#include <iterator>
typedef std::vector< std::pair<int, std::string>> Categories;
int main()
{
std::map<std::string, Categories> cats;
cats["key1"] = {std::make_pair(1, "pair1"), std::make_pair(1, "pair2"), std::make_pair(1, "par3")};
cats["key2"] = {std::make_pair(2, "pair1"), std::make_pair(2, "pair2")};
cats["key3"] = {std::make_pair(3, "pair1")};
std::cout << "Range based loop \n";
for(const auto& it: cats)
{
std::cout << it.first << " = "; // keys
for(const auto& ve: it.second) // values vec
std::cout << ve.first << "-" << ve.second << "\t";
std::cout << std::endl;
}
std::cout << "\nIterator loop \n";
std::map<std::string, Categories>::const_iterator it;
std::vector< std::pair<int, std::string>>::const_iterator curr_val_it;
for(it = cats.cbegin(); it != cats.cend(); ++it)
{
std::cout << it->first << " = "; // keys
for(curr_val_it = it->second.cbegin(); curr_val_it != it->second.cend(); ++curr_val_it )
std::cout << curr_val_it->first << "-" << curr_val_it->second << "\t"; // values vec
std::cout << std::endl;
}
return 0;
}
you need to access a element of the vector first, then the pair within.
... it->second[0].first<< ...
better impl of loop:
for(const auto& cat : cats)
{
string mapidx = cat.first;
vector<pair<int, std::string>> catvect = cat.second;
}
then you can have a seperate loop to read the contents of the vector:
for(const auto& cat : cats)
{
string mapidx = cat.first;
vector<pair<int, std::string>> catvect = cat.second;
for (const auto& entry : catvect)
{
int number = entry.first;
string whatever = entry.second;
}
}
the temp variables are just for readability, no need for all the copies ;)
Error is exacly what compiler told you:
const class std::vector ' has no member named 'first'
so, you have do decide how to print your map by overloading ostream opeartor, below example how it can be achived:
#include <iostream>
#include <map>
#include <string>
#include <utility>
#include <vector>
typedef std::vector<std::pair<int, std::string>> Categories;
std::map<std::string,Categories> cats;
std::ostream& operator << (std::ostream& os, const std::vector<std::pair<int, std::string>>& v)
{
os << "[";
for (auto& el : v) {
os << " " << el.first << " : " << el.second;
}
os << "]";
return os;
}
int main() {
cats.emplace("cat1", std::vector<std::pair<int, std::string>>(1, std::make_pair(1, "category1")));
for(auto& cat : cats) {
std::cout << cat.first << " " << cat.second << "\n";
}
}
Since we are storing Vector Categories inside a Map we will have to iterate Vector too:
for(map<string,Categories>::const_iterator it = cats.begin(); it != cats.end(); ++it)
{
//iterator for vector (it->second)
for(Categories::const_iterator it2 = it->second.begin(); it2 != it->second.end(); it2++ )
{
std::cout << it->first << " " << it2->first<<" "<<it2->second <<"\n";
}
}