I have a map<int, map<int, int>>.
The first key stands for node, the second key stands for an attribute, and the 'deepest' element represents a particular value.
I need to check that element, but doing the following unnecessary adds keys to my map:
map<int, map<int, int>> test;
if (test[4][3] > 5)
{
//do something
}
The alternative as I think of it is
map<int, map<int, int>> test;
if (test.find(4) != test.end())
{
if (test[4].find(3) != test[4].end())
{
if (test[4][3] > 5)
{
//do something
}
}
}
Is there a better way to do this? I don't know if there exists the key [4][3] in the map, and I don't want to unnecessarily add it. Thanks!
This should work - first you search in the first map and saves the returned iterator, then if that iterator is valid you search the map that he points to and saves the result in another iterator, and if he's valid that means that the objects you seek exists and you just check the value of it:
map<int, map<int, int>> test;
map<int, map<int, int>>::iterator it1;
map<int, int>::iterator it2;
if ((it1 = test.find(4)) != test.end())
{
if ((it2 = it1->second.find(3)) != it1->second.end())
{
if (it2->second > 5)
{
//do something
}
}
}
return 0;
Not fundamentally different from Tomer Arazy's suggestion, but making use of C++11's decltype to get rid of repeated type declarations (correcting which can be tedious when you modify the data type of the map):
#include <map>
#include <iostream>
int main()
{
using std::map;
map<int, map<int, int>> test;
test[4][3] = 6;
decltype(test.begin()) outer;
decltype(test.begin()->second.begin()) inner;
if (((outer = test.find(4)) != test.end())
&& ((inner = outer->second.find(3)) != outer->second.end())
&& (inner->second > 5))
std::cout << "Found!" << std::endl;
return 0;
}
(Note that the argument of decltype() isn't evaluated, so this works even if the map is empty.)
This will obviously only work with C++11.
Another thing to mention in the context of C++11 is that std::map has now an at() function similar to std::vector. It returns the value for a given key, and throws an exception if the key doesn't exist. This only a useful idea if you consider the non-existence of the key as an error condition. But if so, you could use
if (test.at(4).at(3) > 5)
std::cout << "Found!" << std::endl;
And then possibly catch the std::out_of_range exception somewhere.
I think this might make it a bit easier to see what's going on:
map<int, map<int, int>> test;
map<int, map<int, int>>::iterator outer = test.find(4);
if (outer != test.end())
{
map<int, int>::iterator inner = outer->second.find(3);
if (inner != outer->second.end())
{
if (inner->second > 5)
{
//do something
}
}
}
return 0;
Related
In c++ I need to search a vector containing a pair, in reverse, by the string. I cannot use a map because the strings are not unique and order is important. I then want to return a forward iterator if the string is found or the end iterator if the string is not found.
Please see below for my current code. I have no problem when the string is found but, if the string is not found, I get a segfault in the conditional statement in main.
vector<pair<string, int>>::iterator prev_it(const string& pred,
vector<pair<string, int>> prevpreds) {
vector<pair<string, int>>::reverse_iterator rit;
for(rit = prevpreds.rbegin();
rit != prevpreds.rend(); ++rit) {
if (rit->first == pred) {
return (rit+1).base();}
}
if(rit == prevpreds.rend()) {
return prevpreds.end();
}
}
and in main:
int main() {
vector<pair<string, int>> test;
for(int i = 0; i <= 5; ++i) {
pair<string, int> mypair;
mypair = make_pair("X"+to_string(i%4+1), i+1);
test.emplace_back(mypair);
}
string tpred = "X"+to_string(6);
vector<pair<string, int>>::iterator tit;
tit = prev_it(tpred, test);
if (tit != test.end()) {
cout << tit->first << " " << tit->second << endl;
}
else {cout << "This is the end." << endl;}
}
The code works if tpred is one of X1 to X4. If tpred is X6 (i.e. not an element of test) then I get a segfault. What I would like to be able to do is return the end forward iterator and then, as in main(), have a conditional based on this.
Edit: I am new to c++ (about a year). I am returning a forward iterator because I need to use the iterator later and this seems clearer (but I could be wrong). As far as I understand, a multimap allows non-unique keys but will order the unique keys. I should have been clearer and said time order was important, not key order. I prefer not to use auto while developing because I like to see what I container element/iterator I am using, but point taken.
You're using an iterator of a destructed object. Pass prevpreds by reference, so the iterator maintains valid.
vector<pair<string, int>>::const_iterator prev_it(const string& pred,
const vector<pair<string, int>> &prevpreds)
{
vector<pair<string, int>>::const_reverse_iterator rit;
for (rit = prevpreds.rbegin();
rit != prevpreds.rend(); ++rit)
{
if (rit->first == pred)
{
return (rit + 1).base();
}
}
return prevpreds.end();
}
int main()
{
// ...
vector<pair<string, int>>::const_iterator tit; // <-- uses const iterator
tit = prev_it(tpred, test);
// ...
}
I'm new to stl in c++ and I want to iterate over a vector of maps. Then, if one of these maps satisfies a certain condition, I want to make a copy of this map an then insert the copy into the vector.
For example:
int main(){
vector<map<string, int> > my_vector;
map<string, int> my_map;
my_map["zero"] = 0;
my_vector.push_back(my_map);
for(vector<map<string, int> >::iterator iter1 = my_vector.begin();
iter1 != my_vector,end();
iter1++){
for(map<string, int>::iterator iter2 = (*iter1).begin();
iter2 != (*iter1).end();
iter2++){
if(iter2->second == 0){
// make a copy of the map (*iter1), make some changes on it,
// then insert the copy in the vector
}
}
}
return 0;
}
I have tried with:
map<string, int> new_map = *iter1;
new_map["zero"]++; // to avoid an infinite loop
my_vector.push_back(new_map);
But the program crashes. No compiler error, only a program crash.
Then I found this question and answers in stackoverflow, so I tried another way like this:
int main(){
vector<map<string, int> > my_vector;
map<string, int> my_map;
my_map["zero"] = 0;
my_vector.push_back(my_map);
int remaining = my_vector.size();
int current_position = 0;
while(remaining>0){
for(map<string, int>:: iterator iter1 = my_vector[index].begin();
iter1 != my_vector[index].end();
iter1++){
if(iter1->second == 0){
map<string, int> new_map = my_vector[0];
new_map["zero"]++; // to avoid an infinite loop
my_vector.push_back(new_map);
}
}
index++;
remaining = my_vector.size()-index;
}
return 0;
}
But the program is still crashing, so I think the problem (or one of the problems) would be not only the iterator, but also the "copy" of the map.
If anyone have an idea of how I should do this, I will really appreciate it.
Your program may crash because push_back can invalidate iterators. For example if a call to push_back leads to memory reallocation (it happens when you exceed current capacity of the vector) then all vector's iterators become invalid (they point to deallocated memory).
To solve that problem you can use indexes instead of iterators to access the vector's elements or you can push_back new elements to another copy of the vector.
In other words: don't use an old vector::iterator after you push_back'ed a new element into the vector.
One way you can do is:
size_t orig_size = my_vector.size();
for( size_t i = 0; i < orig_size; i++ ) {
//...
}
Other way:
Build a new vector and then afterwards append the contents of this new vector to the original vector.
I probably have the wrong idea, but I think you can do something like this. You iterate over the elements of the vector, use the predicate to see if a current element fits the criteria, then use back_inserter.
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
int main(){
vector<map<string, int> > my_vector;
map<string, int> my_map;
my_map["zero"] = 0;
my_vector.push_back(my_map);
auto predicate = [](const map<string, int>& cur) {
for (const auto& pair : cur)
{
if (pair.second == 0)
return true;
}
return false;
};
std::copy_if(my_vector.begin(), my_vector.end(), back_inserter(my_vector), predicate);
return 0;
}
This is my solution. It uses C++11 (because it looks so much better). Especially with collections C++11 gives the workflow a real boost.
#include <vector>
#include <string>
#include <map>
#include <iostream>
using namespace std ;
void merge(vector<map<string,int>>& from, vector<map<string,int>>& to)
{
for(auto entry : from) {
to.push_back(entry) ;
}
}
void search(vector<map<string,int>>& vec)
{
int iCount = 0 ;
vector<map<string, int>> cVector ;
for(auto vmap : vec) {
for(auto mapentry : vmap) {
if(mapentry.second == 0) {
iCount++ ;
map<string, int> new_map = vmap ;
cVector.push_back(new_map) ;
}
}
}
cout << "iCount: " << iCount << endl ;
merge(cVector, vec) ;
}
int main(){
vector<map<string, int> > my_vector;
map<string, int> my_map;
my_map["zero"] = 0;
my_map["third"] = 2 ;
my_map["second"] = 0 ;
my_vector.push_back(my_map);
cout << my_vector.size() << endl ;
vector<map<string,int>> cVector ;
search(my_vector) ;
cout << my_vector.size() << endl ;
return 0;
}
As written by Alex Antonov the push_back invalidates your Iterator because the memory gets reallocated. My answer creates a new vector and copies the entries after the search back in the original vector (my_vector).
About range base for-loops see this.
I have a map and I want the first column i.e (*it).first to be pushed back into a vector then (*it)->second to be pushed back into another vector
Is this the best way to do it?
std::vector<std::string>test;
for ( it=mymap.begin() ; it != mymap.end(); it++ )
{
test.push_back((*it).first);
}
My other question is if i have a loop i.e
how would I insert all the integers i into (*it).first?
for(int i = 0; i < 10; i++)
{
// 1 - 10 will go in (*it).first
}
I want to have some integers in (*it).first and have associated values in (*it).second;
Use std::transform.
First define two functions key and value which take the pair of strings and return the first or second value, respectively.
#include <map>
#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator>
const std::string& key(const std::pair<std::string, std::string>& keyValue)
{
return keyValue.first;
}
const std::string& value(const std::pair<std::string, std::string>& keyValue)
{
return keyValue.second;
}
Then use std::transform from <algorithm> with the functions to transform the map into either a vector of keys or a vector of values.
int main()
{
using namespace std; // be explicit normally, trying to be brief here
map<string, string> contacts;
contacts["alice"] = "555-2701";
contacts["bob"] = "555-2702";
vector<string> keys(contacts.size());
vector<string> values(contacts.size());
transform(contacts.begin(), contacts.end(), keys.begin(), key);
transform(contacts.begin(), contacts.end(), values.begin(), value);
cout << "Keys:\n";
copy(keys.begin(), keys.end(), ostream_iterator<string>(cout, "\n"));
cout << "\n";
cout << "Values:\n";
copy(values.begin(), values.end(), ostream_iterator<string>(cout, "\n"));
return 0;
}
Output:
Keys:
alice
bob
Values:
555-2701
555-2702
Your first question, "how can I push the first column of my map into one vector and the 2nd column into another" is solved thus:
std::map<std::string, std::string> mymap;
std::vector<std::string> keys;
std::vector<std::string> values;
for ( std::map<std::string,std::string>::iterator it=mymap.begin() ; it != mymap.end(); ++it )
{
keys.push_back(it->first);
values.push_back(it->second);
}
Your second question, "how would insert all the integers i into (*it).first ?" is solved thus:
std::map<int, int> mymap2;
for(int i = 0; i < 10; i++)
{
// Insert default value into map
// This sets '(*it).first' to 'i' and
// '(*it).second' to a default value (in
// this case, 0).
mymap2[i];
}
or
std::map<int, int> mymap3;
for(int i = 0; i < 10; i++)
{
// Insert specified value into map
// this sets '(*it).first' to 'i', and
// '(*it).second' to the value returned from the function.
maymap3[i] = ChooseSpecificValue(i);
}
Well, it can be done with a simple loop:
for (auto const& p: mymap) {
vec1.push_back(p.first);
vec2.push_back(p.second);
}
Or using the std::transform algorithm, though it's quite verbose here:
std::transform(mymap.begin(), mymap.end(), std::back_inserter(vec1),
[](MyMap::const_reference p) { return p.first; });
Assuming you've declared your map as string key and value (ie map<string, string> mymap; then it would be like below, also assuming you've declare 'it' variable as map<string, string>::iterator it, etc:
std::vector<std::string> test;
std::vector<std::string> second;
std::map<string, string>::iterator it;
for ( it=mymap.begin() ; it != mymap.end(); it++ )
{
test.push_back((*it).first);
second.push_back((*it).second);
}
Not sure about your next question.
The first part of your question:
std::vector<std::string> test;
std::vector<std::string> test2; // assuming map is from string to string
for (it = mymap.begin(); it != mymap.end(); ++it)
{
test.push_back(it->first); // push first in one vector
test2.push_back(it->second); // push second in another vector
}
So, yes a simple for can do what you want.
The second part of your question:
Since you are updating the key of the map, you would need to remove it from the map and insert the changed one. So:
std::string first, second;
first = it->first;
second = it->second;
mymap.erase(it); // be careful with invalidating iterator
// change first
mymap[first] = second;
To change first by adding all integers i to it, that would really depend on the type of first. For example with a string, you may mean something like this:
ostringstream sout;
for (int i = 0; i < 10; ++i)
sout << (i?" ":"") << i;
first = sout.str();
Or if first is for example a set, you may mean something like this:
for (int i = 0; i < 10; ++i)
first.insert(i);
and my other question is if i have a loop i.e how would insert all the
integers i into (*it).first?
In the case of a std::map, you can't modify the iterator returned like that ... the key member (i.e., the first) in the std::map key/value pair data-structure is intentionally designated as a constant value, and is initialized to its constant value at the beginning of the key/value pair's lifetime in the std::map data-structure. If the keys weren't constant, you would end up creating havoc when you change the key, since the nodes in a std::map are suppose to be sorted by the keys. The second member of the key/value pair data-structure is the member that can be changed.
So if you want to insert a set of key/value pairs in a map, you could simply do the following:
std::map<int, int> mymap;
int some_other_value = 100;
for (int i=0; i < 10; i++)
{
mymap[i] = some_other_value++;
}
it here will be an iterator which will point to one of the position in map and at max have one first and second value for one iterator . At max you can have multiple key or same key holding same/different values depending on key/value combination.
As far as pushing the value in the vector for a key in map is concern you can do it in the same way you are pushing the key
std::vector<std::string>test;
std::vector<std::string>test2;
for ( it=mymap.begin() ; it != mymap.end(); it++ )
{
test.push_back((*it).first);
test2.push_back((*it).second);
}
Neways yours question is very unclear .
Just in case you want to deal with different data types in your map I would template a generic copy function:
template <class A, class B>
void mycopy(std::map<A, B>&m, std::list<A>& keys, std::list<B>& values) {
typename std::map<A, B>::iterator it;
for (it = m.begin(); it != m.end(); ++it) {
keys.push_back( (*it).first );
values.push_back( (*it).second );
}
}
Mixing it up:
std::map<int, std::string> mymap;
std::list<int> keys;
std::list<std::string> values;
mymap[1] = "string1";
mymap[2] = "string2";
mycopy(mymap, keys, values);
std::map<std::string, int> mymap1;
std::list<std::string> keys1;
std::list<int> values1;
mymap1["string1"] = 1;
mymap1["string2"] = 2;
mycopy(mymap1, keys1, values1);
Edit: yes __copy isnt the best definition. Thanks
I have a 3D map container declared as follows:
std::map<std::string, std::map<std::string, std::map<std::string, CGridItem*> > > m_3DGridItems;
Suppose I have a CGridItem object pointer value, how can I get all the three map key strings in an efficient way? Thank you!
First of all, do you really need such a clumsy container ?
It would be much easier to have a Key structure:
struct Key {
std::string x;
std::string y;
std::string z;
};
And then define an ordering on Key:
bool operator<(Key const& left, Key const& right) {
if (left.x < right.x) { return true; }
if (left.x > right.x) { return false; }
if (left.y < right.y) { return true; }
if (left.y > right.y) { return false; }
return left.z < right.z;
}
Then you can have a much easier structure to manipulate:
std::map<Key, GridItem*>
If you need to map both ways, check out Boost.Bimap which maintains a two-ways mapping Key <-> GridItem* (so you don't have to sync two structures yourself).
You can just use iterator to get all key/value in a map. When the value is a map too, you can get the key/values the same way ...
First thing: if you are primarily making look-ups like that, this data-structure is definitely not the best performing alternative.
I don't see any other way than to make three nested for loops, since the map is laid out to make look-ups by key and not by value. It would look something like this:
std::map<std::string, std::map<std::string, std::map<std::string, CGridItem*> > >:iterator it1;
CGridItem* obj = ...;
for(it1 = mymap.begin(); it != mymap.end(); ++it1)
{
std::map<std::string, std::map<std::string, CGridItem*> > it2;
for(it2 = it1->second.begin(); it2 != it->second.end(); ++it2)
{
std::map<std::string, CGridItem*> it3;
for(it3 = it2->second.begin(); it3 != it2->second.end(); ++it3)
{
if(it3->second == obj) {
/*found it!*/
/* your 3 strings are in it1->first, it2->first, it3->first */
}
}
}
}
EDIT: I propose the following data structure:
std::map<CGridItem*, std::tuple<std::string, std::string, std::string> > mymap;
This maps your CGridItem object to 3 strings. Note: the std::tuple may not be available when you are not using c++11, but it is available in the boost libraries.
I have an STL map definition as follows:
map<string, map<int, string> > info;
I iterate that map using the following code:
for( map<string, map<int, string> >::iterator ii=info.begin(); ii!=info.end(); ++ii){
for(map<int, string>::iterator j=ii->second.begin(); j!=ii->second.end();++j){
cout << (*ii).first << " : " << (*j).first << " : "<< (*j).second << endl;
}
}
Is this the correct way to iterate or is there a better way to do so? The above code works for me, but I'm looking for a more elegant solution.
This is correct, it just lacks a few typedef and readability improvements :
typedef std::map<int, std::string> inner_map;
typedef std::map<std::string, inner_map> outer_map;
for (outer_map::iterator i = outerMap.begin(), iend = outerMap.end(); i != iend; ++i)
{
inner_map &innerMap = i->second;
for (inner_map::iterator j = innerMap.begin(), jend = innerMap.end(); j != jend; ++j)
{
/* ... */
}
}
If C++11 is available you may use range for loop:
for(auto &i: info) {
for(auto &j: i.second) {
/* */
}
}
If only C++11 auto is available:
for( auto i=info.begin(); i!=info.end(); ++i) {
for( auto j=i->second.begin(); j!=i->second.end(); ++j) {
/* */
}
}
If you may use BOOST there is BOOST_FOREACH:
typedef std::map<int, std::string> inner_map;
typedef std::map<std::string, inner_map> outer_map;
outer_map outer;
BOOST_FOREACH(outer_map::value_type &outer_value, outer){
BOOST_FOREACH(inner_map::value_type &inner_value, outer_value->second){
/* use outer_value and inner_value as std::pair */
}
}
While it's not clear what problem you are solving by having a map inside a map, I don't think there is a better way of iterating on all the items without using these iterators. The only thing you can do to improve code readability is to use typedefs on the template types.
However, won't it be a better idea to define your map as
multimap <string, MyClass>
where MyClass is defined as a pair of integer and a string, as well as a toString() method to dump the contents, etc?
If c++11 is available, we could use stl algorithm for_each and lambda functions to get a elegant solution
typedef map<int, string> INNERMAP;
typedef map<string, INNERMAP> OUTERMAP;
OUTERMAP theMapObject;
// populate the map object
// iterate the map object now
std::for_each(theMapObject.cbegin(), theMapObject.cend(),
[](const OUTERMAP::value_type& outerMapElement)
{
// process the outer map object
const INNERMAP& innerMapObject = outerMapElement.second;
std::for_each(innerMapObject.cbegin(), innerMapObject.cend(),
[](const INNERMAP::value_type& innermapElemen)
{
//process the inner map element
});
});
If you want to iterate through both maps, then the way you presented is the best way. Now, if there is something specific you want to do then you might be better with using a function from the algorithm header.
If you have access to C++11 features, then range-based for loops as proposed in Juraj Blaho's answer seem to be the most readable option to me. However, if C++17 is available to you, then you can use structured bindings together with those loops to further increase readability, as you can get rid of all first and second members:
std::map<std::string, std::map<int, std::string>> info;
for (const auto &[k1, v1] : info) {
for (const auto &[k2, v2] : v1) {
std::cout << k1 << " : " << k2 << " : " << v2 << std::endl;
}
}
Code on Coliru