I want to traverse an STL map. I don't want to use its key. I don't care about the ordering, I just look for a way to access all elements it contains. How can I do this?
Yes, you can traverse a Standard Library map. This is the basic method used to traverse a map, and serves as guidance to traverse any Standard Library collection:
C++03/C++11:
#include <cstdlib>
#include <map>
#include <string>
using namespace std;
int main()
{
typedef map<int,string> MyMap;
MyMap my_map;
// ... magic
for( MyMap::const_iterator it = my_map.begin(); it != my_map.end(); ++it )
{
int key = it->first;
string value = it->second;
}
}
If you need to modify the elements:
Use iterator rather than const_iterator.
Instead of copying the values out of the iterator, get a reference and modify the values through that.
for( MyMap::iterator it = my_map.begin(); it != my_map.end(); ++it )
{
int key = it->first;
string& value = it->second;
if( value == "foo" )
value = "bar";
}
This is how you typically traverse Standard Library containers by hand. The big difference is that for a map the type of *it is a pair rather than the element itself
C++11
If you have the benefit of a C++11 compiler (for example, latest GCC with --std=c++11 or MSVC), then you have other options as well.
First you can make use of the auto keyword to get rid of all that nasty verbosity:
#include <cstdlib>
#include <map>
#include <string>
using namespace std;
int main()
{
map<int,string> my_map;
// ... magic
for( auto it = my_map.begin(); it != my_map.end(); ++it )
{
int key = it->first;
string& value = it->second;
}
}
Second, you can also employ lambdas. In conjunction with decltype, this might result in cleaner code (though with tradeoffs):
#include <cstdlib>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
map<int,string> my_map;
// ... magic
for_each(my_map.begin(), my_map.end(), [](decltype(*my_map.begin()) val)
{
string& value = val.second;
int key = val.first;
});
}
C++11 also instroduces the concept of a range-bases for loop, which you may recognize as similar to other languages. However, some compilers do not fully support this yet -- notably, MSVC.
#include <cstdlib>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
map<int,string> my_map;
// ... magic
for(auto val : my_map )
{
string& value = val.second;
int key = val.first;
}
}
As with any STL container, the begin() and end() methods return iterators that you can use to iterate over the map. Dereferencing a map iterator yields a std::pair<const Key, Value>.
C++17
Since C++17 you can use range-based for loops together with structured bindings for iterating over a map. The resulting code, e.g. for printing all elements of a map, is short and well readable:
std::map<int, std::string> m{ {3, "a"}, {5, "b"}, {9, "c"} };
for (const auto &[k, v] : m)
std::cout << "m[" << k << "] = " << v << std::endl;
Output:
m[3] = a
m[5] = b
m[9] = c
Code on Coliru
You can traverse STL map in the same way as any other STL container: using iterators, e.g.
for (std::map<key, value>::const_iterator
i = myMap.begin(), end = myMap.end(); i != end; ++i)
{
// *i is a key-value pair
}
Using for with auto for C++11 and above usage
map<int,int> map_variable; //you can use any data type for keys, as well as value
for(auto &x:map_variable)
{
cout<<x.first ;// gives the key
cout<<x.second; //gives the value
}
The newer format of for using auto was introduced in C++11
To give it functionality like some higher level languages like python
Where there was already an implementation of such type of iteration
P.S. : map variable keeps values sorted, so when iterating you will get keys in sorted order
You can iterate map by using auto iterator.
Code Snippet:
#include<bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
map<string, int> mp;
mp["a"]=500;
mp["b"]=200;
mp["d"]=300;
mp["c"]=400;
for(auto it=mp.begin(); it != mp.end(); it++)
{
cout<<it->first <<" : "<<it->second<<endl;
}
return 0;
}
Related
I have a map<string, std::function<void(AgentMessage&)>> (AgentMessage is a struct with a few strings). When I try to access it using an iterator I get an access violation on the copy function of pair.
note: The std::function is pointing at a function in a different dll than the place where it is copied.
EDIT: I thought the explanation was good enough for a simple piece of code, but still - here it is.
for (map<string, std::function<void(AgentMessage&)>>::iterator it = mapRef.begin(); it != mapRef.end(); it++)
{
auto functionCopy = it->second; // IT CRASHES HERE
}
Can you show the code that inserts elements to the map?
I tried this and it works:
#include <functional>
#include <map>
#include <string>
using namespace std;
struct AgentMessage
{
};
void f(AgentMessage& am)
{
}
void g(AgentMessage& am)
{
}
int main()
{
AgentMessage am;
map<string, std::function<void(AgentMessage&)>> m;
m["f"] = f;
m["g"] = g;
for (map<string, std::function<void(AgentMessage&)>>::iterator it = m.begin(); it != m.end(); ++it)
{
auto func = it->second;
func(am);
}
}
Is it possible to iterate over all of the values in a std::map using just a "foreach"?
This is my current code:
std::map<float, MyClass*> foo ;
for (map<float, MyClass*>::iterator i = foo.begin() ; i != foo.end() ; i ++ ) {
MyClass *j = i->second ;
j->bar() ;
}
Is there a way I can do the following?
for (MyClass* i : /*magic here?*/) {
i->bar() ;
}
From C++1z/17, you can use structured bindings:
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<int, std::string> m;
m[1] = "first";
m[2] = "second";
m[3] = "third";
for (const auto & [key, value] : m)
std::cout << value << std::endl;
}
std::map<float, MyClass*> foo;
for (const auto& any : foo) {
MyClass *j = any.second;
j->bar();
}
in c++11 (also known as c++0x), you can do this like in C# and Java
The magic lies with Boost.Range's map_values adaptor:
#include <boost/range/adaptor/map.hpp>
for(auto&& i : foo | boost::adaptors::map_values){
i->bar();
}
And it's officially called a "range-based for loop", not a "foreach loop". :)
Since C++20 you can add the range adaptor std::views::values from the Ranges library to your range-based for loop. This way you can implement a similar solution to the one in Xeo's answer, but without using Boost:
#include <map>
#include <ranges>
std::map<float, MyClass*> foo;
for (auto const &i : foo | std::views::values)
i->bar();
Code on Wandbox
I would like to create and hold on to an iterator_range. The range is constructed based on a predicate (for this example, I look for even numbers).
I can do this, but it seems I must make a copy elements from the underlying vector that is being iterated.
Please look for the comments marked ">>>" in the sample below.
Is there a way to create the iterator_range and NOT have to create a duplicate of entries from the original vector?
I looked, and have not seen, an answer to this particular situation.
#include <vector>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/range.hpp>
#include <boost/foreach.hpp>
#include <boost/iterator/filter_iterator.hpp>
#include <boost/range/iterator_range.hpp>
using namespace std;
using namespace boost;
typedef boost::iterator_range<vector<int>::iterator> int_range;
template< class Range, class Pred >
boost::iterator_range< boost::filter_iterator< Pred, typename boost::range_iterator<Range>::type > >
make_filter_range( Range& rng, Pred pred ) {
return boost::make_iterator_range(
boost::make_filter_iterator(pred, boost::begin(rng), boost::end(rng)),
boost::make_filter_iterator(pred, boost::end(rng), boost::end(rng)) );
}
// This is the predicate evaluation function.
bool IsEvenFilter(int val) { return val % 2 == 0; }
void TestMakeIteratorRange()
{
std::vector<int> vals;
vals.push_back(1);
vals.push_back(4);
vals.push_back(7);
vals.push_back(11);
vals.push_back(16);
vals.push_back(19);
vals.push_back(28);
//>>> The following int_range line does not compile. Why?
//>>> How can I return an int_range?
//>>> int_range intRange = make_filter_range( vals, boost::bind(&IsEvenFilter, _1));
//>>> The following WILL work, but it forces a second copy of elements from vals.
std::vector<int> v2 = boost::copy_range< std::vector<int> >(
make_filter_range( vals, boost::bind(&IsEvenFilter, _1)));
int_range intRange = int_range(v2);
// Dump out contents
BOOST_FOREACH(int &val, intRange)
{
cout << " " << val;
}
cout << endl;
}
void main()
{
TestMakeIteratorRange();
}
int_range intRange = make_filter_range( vals, boost::bind(&IsEvenFilter, _1));
You have to store the type returned by make_filter_range. Which is not int_range.
This is incidentally why auto exists (in C++11); so that you don't have to type that return value if you want to store what the function returns. If you don't have access to C++11 auto, use BOOST_AUTO instead.
If you can't use that for some reason, you can also use any_range. Which, as the name suggests, can store any range for a specific type.
Also, consider using the proper Boost range-adapters, like boost::adaptors::filtered instead of make_filter_iterator.
I have following code:
#include <iostream>
#include <fstream>
#include <algorithm>
#include <iterator>
#include <vector>
#include <functional>
using namespace std;
typedef istream_iterator<string> is_it;
typedef vector<string>::iterator v_str_it;
int main()
{
int i = 4;
ifstream ifstr("1.txt");
is_it ifstrm(ifstr);
is_it eof;
vector<string> v_str(ifstrm, eof);
v_str_it vsit = v_str.begin();
while( (vsit = find_if(vsit, v_str.end(),
bind2nd(equal_to<string>(), i ))) != v_str.end())
{
cout << *vsit << endl;
++vsit;
}
return 0;
}
As far as I understand in find_if(vsit, v_str.end(), bind2nd(equal_to<string>(), i ) i should use const char like "sometext" instead of int i. But how can i find words with length equal to 4 e.g. ? I'm confused and need some advice.
find_if will only return the first item in the sequence that satisfies the predicate.
For this you really want a lambda and if you are using C++11. This will look something like:
[](std::string const& x) { return x.size() == i; }
(Not sure of the exact syntax).
To create a "functor" which is the simplest here you might do:
struct CompareStringLength
{
int len_;
explicit CompareStringLength( int len ) : len_(len)
{
}
bool operator()(std::string const& str ) const
{
return str.size() == len_;
}
};
Within your vector you would now use std::find_if( v.begin(), v.end(), CompareStringLength(i) );
to get the first element. To find all of them there is no std::copy_if to copy them into another vector so you'd actually have to create a different predicate that returns the opposite and use remove_copy_if which does exist or write your own copy_if algorithm.
I need to write to a bunch of files simultaneously, so I decided to use map <string, ofstream>.
map<string, ofstream> MyFileMap;
I take a vector<string> FileInd, which consists of, say "a" "b" "c", and try to open my files with:
for (vector<string>::iterator it = FileInd.begin(); iter != FileInd.end(); ++it){
...
MyFileMap[*it].open("/myhomefolder/"+(*it)+".");
}
I get the error
request for member 'open' in ..... , which is of non-class type 'std::ofstream*'
I've tried to switch to
map<string, ofstream*> MyFileMap;
But it didn't work either.
Could anyone help?
Thanks.
Clarification:
I've tried both
map<string, ofstream> MyFileMap;
map<string, ofstream*> MyFileMap;
with both
.open
->open
neither of 4 variants work.
Solution (suggested in Rob's code below):
Basically, I forgot "new", the following works for me:
map<string, ofstream*> MyFileMap;
MyFileMap[*it] = new ofstream("/myhomefolder/"+(*it)+".");
std::map<std::string, std::ofstream> can't possibly work, because std::map requires its data type to be Assignable, which std::ofstream isn't. In the alternative, the data type must be a pointer to ofstream -- either a raw pointer or a smart pointer.
Here is how I would do it, using C++11 features:
#include <string>
#include <map>
#include <fstream>
#include <iostream>
#include <vector>
int main (int ac, char **av)
{
// Convenient access to argument array
std::vector<std::string> fileNames(av+1, av+ac);
// If I were smart, this would be std::shared_ptr or something
std::map<std::string, std::ofstream*> fileMap;
// Open all of the files
for(auto& fileName : fileNames) {
fileMap[fileName] = new std::ofstream("/tmp/xxx/"+fileName+".txt");
if(!fileMap[fileName] || !*fileMap[fileName])
perror(fileName.c_str());
}
// Write some data to all of the files
for(auto& pair : fileMap) {
*pair.second << "Hello, world\n";
}
// Close all of the files
// If I had used std::shared_ptr, I could skip this step
for(auto& pair : fileMap) {
delete pair.second;
pair.second = 0;
}
}
and the 2nd verse, in C++03:
#include <string>
#include <map>
#include <fstream>
#include <iostream>
#include <vector>
int main (int ac, char **av)
{
typedef std::map<std::string, std::ofstream*> Map;
typedef Map::iterator Iterator;
Map fileMap;
// Open all of the files
std::string xxx("/tmp/xxx/");
while(av++,--ac) {
fileMap[*av] = new std::ofstream( (xxx+*av+".txt").c_str() );
if(!fileMap[*av] || !*fileMap[*av])
perror(*av);
}
// Write some data to all of the files
for(Iterator it = fileMap.begin(); it != fileMap.end(); ++it) {
*(it->second) << "Hello, world\n";
}
// Close all of the files
for(Iterator it = fileMap.begin(); it != fileMap.end(); ++it) {
delete it->second;
it->second = 0;
}
}