Copy map values to vector in STL [duplicate] - c++

This question already has answers here:
How to retrieve all keys (or values) from a std::map and put them into a vector?
(24 answers)
Closed 1 year ago.
Working my way through Effective STL at the moment. Item 5 suggests that it's usually preferable to use range member functions to their single element counterparts. I currently wish to copy all the values in a map (i.e. - I don't need the keys) to a vector.
What is the cleanest way to do this?

You could probably use std::transform for that purpose. I would maybe prefer Neils version though, depending on what is more readable.
Example by xtofl (see comments):
#include <map>
#include <vector>
#include <algorithm>
#include <iostream>
template< typename tPair >
struct second_t {
typename tPair::second_type operator()( const tPair& p ) const { return p.second; }
};
template< typename tMap >
second_t< typename tMap::value_type > second( const tMap& m ) { return second_t< typename tMap::value_type >(); }
int main() {
std::map<int,bool> m;
m[0]=true;
m[1]=false;
//...
std::vector<bool> v;
std::transform( m.begin(), m.end(), std::back_inserter( v ), second(m) );
std::transform( m.begin(), m.end(), std::ostream_iterator<bool>( std::cout, ";" ), second(m) );
}
Very generic, remember to give him credit if you find it useful.

You can't easily use a range here because the iterator you get from a map refers to a std::pair, where the iterators you would use to insert into a vector refers to an object of the type stored in the vector, which is (if you are discarding the key) not a pair.
I really don't think it gets much cleaner than the obvious:
#include <map>
#include <vector>
#include <string>
using namespace std;
int main() {
typedef map <string, int> MapType;
MapType m;
vector <int> v;
// populate map somehow
for( MapType::iterator it = m.begin(); it != m.end(); ++it ) {
v.push_back( it->second );
}
}
which I would probably re-write as a template function if I was going to use it more than once. Something like:
template <typename M, typename V>
void MapToVec( const M & m, V & v ) {
for( typename M::const_iterator it = m.begin(); it != m.end(); ++it ) {
v.push_back( it->second );
}
}

With C++11 we have the fancy new for loop:
for (const auto &s : schemas)
names.push_back(s.second);
where schemas is a std::map and names is an std::vector.
This populates the array (names) with values from the map (schemas); change s.second to s.first to get an array of keys.

#include <algorithm> // std::transform
#include <iterator> // std::back_inserter
std::transform(
your_map.begin(),
your_map.end(),
std::back_inserter(your_values_vector),
[](auto &kv){ return kv.second;}
);
Sorry that I didn't add any explanation - I thought that code is so simple that is doesn't require any explanation.
So:
transform( beginInputRange, endInputRange, outputIterator, unaryOperation)
this function calls unaryOperation on every item from inputIterator range (beginInputRange-endInputRange). The value of operation is stored into outputIterator.
If we want to operate through whole map - we use map.begin() and map.end() as our input range. We want to store our map values into vector - so we have to use back_inserter on our vector: back_inserter(your_values_vector). The back_inserter is special outputIterator that pushes new elements at the end of given (as paremeter) collection.
The last parameter is unaryOperation - it takes only one parameter - inputIterator's value. So we can use lambda:
[](auto &kv) { [...] }, where &kv is just a reference to map item's pair. So if we want to return only values of map's items we can simply return kv.second:
[](auto &kv) { return kv.second; }
I think this explains any doubts.

If you are using the boost libraries, you can use boost::bind to access the second value of the pair as follows:
#include <string>
#include <map>
#include <vector>
#include <algorithm>
#include <boost/bind.hpp>
int main()
{
typedef std::map<std::string, int> MapT;
typedef std::vector<int> VecT;
MapT map;
VecT vec;
map["one"] = 1;
map["two"] = 2;
map["three"] = 3;
map["four"] = 4;
map["five"] = 5;
std::transform( map.begin(), map.end(),
std::back_inserter(vec),
boost::bind(&MapT::value_type::second,_1) );
}
This solution is based on a post from Michael Goldshteyn on the boost mailing list.

Using lambdas one can perform the following:
{
std::map<std::string,int> m;
std::vector<int> v;
v.reserve(m.size());
std::for_each(m.begin(),m.end(),
[&v](const std::map<std::string,int>::value_type& p)
{ v.push_back(p.second); });
}

Here is what I would do.
Also I would use a template function to make the construction of select2nd easier.
#include <map>
#include <vector>
#include <algorithm>
#include <memory>
#include <string>
/*
* A class to extract the second part of a pair
*/
template<typename T>
struct select2nd
{
typename T::second_type operator()(T const& value) const
{return value.second;}
};
/*
* A utility template function to make the use of select2nd easy.
* Pass a map and it automatically creates a select2nd that utilizes the
* value type. This works nicely as the template functions can deduce the
* template parameters based on the function parameters.
*/
template<typename T>
select2nd<typename T::value_type> make_select2nd(T const& m)
{
return select2nd<typename T::value_type>();
}
int main()
{
std::map<int,std::string> m;
std::vector<std::string> v;
/*
* Please note: You must use std::back_inserter()
* As transform assumes the second range is as large as the first.
* Alternatively you could pre-populate the vector.
*
* Use make_select2nd() to make the function look nice.
* Alternatively you could use:
* select2nd<std::map<int,std::string>::value_type>()
*/
std::transform(m.begin(),m.end(),
std::back_inserter(v),
make_select2nd(m)
);
}

One way is to use functor:
template <class T1, class T2>
class CopyMapToVec
{
public:
CopyMapToVec(std::vector<T2>& aVec): mVec(aVec){}
bool operator () (const std::pair<T1,T2>& mapVal) const
{
mVec.push_back(mapVal.second);
return true;
}
private:
std::vector<T2>& mVec;
};
int main()
{
std::map<std::string, int> myMap;
myMap["test1"] = 1;
myMap["test2"] = 2;
std::vector<int> myVector;
//reserve the memory for vector
myVector.reserve(myMap.size());
//create the functor
CopyMapToVec<std::string, int> aConverter(myVector);
//call the functor
std::for_each(myMap.begin(), myMap.end(), aConverter);
}

Why not:
template<typename K, typename V>
std::vector<V> MapValuesAsVector(const std::map<K, V>& map)
{
std::vector<V> vec;
vec.reserve(map.size());
std::for_each(std::begin(map), std::end(map),
[&vec] (const std::map<K, V>::value_type& entry)
{
vec.push_back(entry.second);
});
return vec;
}
usage:
auto vec = MapValuesAsVector(anymap);

I thought it should be
std::transform( map.begin(), map.end(),
std::back_inserter(vec),
boost::bind(&MapT::value_type::first,_1) );

We should use the transform function from STL algorithm, the last parameter of transform function could be a function object, function pointer or a lambda function that convert item of map to item of vector. This case map have items have type pair that need to convert to item that has int type for vector. Here is my solution that I use lambda function:
#include <algorithm> // for std::transform
#include <iterator> // for back_inserted
// Map of pair <int, string> need to convert to vector of string
std::map<int, std::string> mapExp = { {1, "first"}, {2, "second"}, {3, "third"}, {4,"fourth"} };
// vector of string to store the value type of map
std::vector<std::string> vValue;
// Convert function
std::transform(mapExp.begin(), mapExp.end(), std::back_inserter(vValue),
[](const std::pair<int, string> &mapItem)
{
return mapItem.second;
});

The other answers mention std::transform, and semantically it's the right choice. But in practice std::accumulate might fit better for this task, because:
it allows adding const to the resulting vector;
it just looks nicer, truly functional-style.
Example (using C++17 syntax):
#include <numeric> // for std::accumulate. Note that it's not in <algorithm> where std::transform is located, thanks to Anton Krug for pointing this out
auto map = std::map<int,bool>{};
map[0]=true;
map[1]=false;
const auto mapValues = std::accumulate(map.begin(), map.end(), std::vector<bool>(map.size()), [](auto& vector, const auto& mapEntry) {
vector.push_back(mapEntry.second);
return vector;
});

Surprised nobody has mentioned the most obvious solution, use the std::vector constructor.
template<typename K, typename V>
std::vector<std::pair<K,V>> mapToVector(const std::unordered_map<K,V> &map)
{
return std::vector<std::pair<K,V>>(map.begin(), map.end());
}

Related

c++ get std::vector<int> from std::vector<std::tuple<int, float>>

i want to initialize a const std::vector<int> member variable in the initializer list of a constructor, given a std::vector<std::tuple<int, float>> constructor argument. The vector should contain all the first tuple items.
Is there a one-liner that extracts an std::vector<int> containing all the first tuple entries from an std::vector<std::tuple<int, float>> ?
With C++20 ranges:
struct X
{
const std::vector<int> v;
template <std::ranges::range R>
requires std::convertible_to<std::ranges::range_value_t<R>, int>
X(R r)
: v{r.begin(), r.end()}
{}
X(const std::vector<std::tuple<int, float>>& vt)
: X{vt | std::ranges::views::elements<0>}
{}
};
With ranges-v3:
struct X
{
const std::vector<int> v;
X(const std::vector<std::tuple<int, float>>& vt)
: v{vt | ranges::views::transform([] (auto t) {
return std::get<0>(t); })
| ranges::to<std::vector>()}
{}
};
And a Frankenstein's monster:
#include <ranges>
#include <range/v3/range/conversion.hpp>
struct X
{
const std::vector<int> v;
X(const std::vector<std::tuple<int, float>>& vt)
: v{vt | std::ranges::views::elements<0>
| ranges::to<std::vector>()}
{}
};
Not a one-liner to setup, but certainly one to use - you can write a function to do the conversion, and then the constructor can call that function when initializing the vector member, eg:
std::vector<int> convertVec(const std::vector<std::tuple<int, float>> &arg)
{
std::vector<int> vec;
vec.reserve(arg.size());
std::transform(arg.begin(), arg.end(), std::back_inserter(vec),
[](const std::tuple<int, float> &t){ return std::get<int>(t); }
);
return vec;
}
struct Test
{
const std::vector<int> vec;
Test(const std::vector<std::tuple<int, float>> &arg)
: vec(convertVec(arg))
{
}
};
Adding to #bolov's answer, let's talk about what you might have liked to do, but can't in a one-liner.
There's the to<>() function from ranges-v3 from #bolov 's answer - it materializes a range into an actual container (a range is lazily-evaluated, and when you create it you don't actually iterate over the elements). There is no reason it shouldn't be in the standard library, if you ask me - maybe they'll add it 2023?
You may be wondering - why do I need that to()? Can't I just initialize a vector by a range? And the answer is - sort of, sometimes, maybe. Consider this program:
#include <vector>
#include <tuple>
#include <ranges>
void foo()
{
using pair_type = std::tuple<int, double>;
std::vector<pair_type> tvec {
{12, 3.4}, {56, 7.8}, { 90, 91.2}
};
auto tv = std::ranges::transform_view(
tvec,
[](const pair_type& p) { return std::get<0>(p);}
);
std::vector<int> vec1 { tv.begin(), tv.end() };
std::vector<std::tuple<int>> vec2 {
std::ranges::transform_view{
tvec,
[](const pair_type& p) { return std::get<0>(p);}
}.begin(),
std::ranges::transform_view{
tvec,
[](const pair_type& p) { return std::get<0>(p);}
}.end()
};
}
The vec1 statement will compile just fine, but the vec2 statement will fail (GodBolt.org). This is surprising (to me anyway)!
You may also be wondering why you even need to go through ranges at all. Why isn't there a...
template <typename Container, typename UnaryOperation>
auto transform(const Container& container, UnaryOperation op);
which constructs the transformed container as its return value? That would allow you to write:
std::vector<int> vec3 {
transform(
tvec,
[](const pair_type& p) { return std::get<0>(p); }
)
}
... and Bob's your uncle. Well, we just don't have functions in the C++ standard library which take containers. It's either iterator pairs, which is the "classic" pre-C++20 standard library, or ranges.
Now, the way I've declared the transform() function, it is actually tricky/impossible to implement generally, since you don't have a way of converting the type of a container to the same type of container but with a different element. So, instead, let's write something a little easier:
template <
typename T,
template <typename> class Container,
typename UnaryOperation
>
auto transform(const Container<T>& container, UnaryOperation op)
{
auto tv = std::ranges::transform_view(container, op);
using op_result = decltype(op(*container.begin()));
return Container<op_result> { tv.begin(), tv.end() };
}
and this works (GodBolt.org).
Yes, there is a one-liner if you use this library in Github.
The code goes like this.
#include <iostream>
#include <tuple>
#include <vector>
#include <LazyExpression/LazyExpression.h>
using std::vector;
using std::tuple;
using std::ref;
using std::cout;
using LazyExpression::Expression;
int main()
{
// Define test data
vector<tuple<int, float>> vecTuple { {1,1.1}, {2,2.2}, {3, 3.3} };
// Initialize int vector with the int part of the tuple (the one-liner :)
vector<int> vecInt = Expression([](const tuple<int, float>& t) { return std::get<int>(t); }, ref(vecTuple))();
// print the results
for (int i : vecInt)
cout << i << ' ';
cout << "\n";
}
// Output: 1 2 3

create container from another container, applying each element some function in c++

My question is simple, see example:
std::array<int,6> a = {{0,1,2,3,4,5}}; // -- given container.
auto F = []( int i ) { return i*i; }; // -- given function.
std::vector<int> v; // need create
// my solution:
v.reserve( a.size () );
for( std::size_t i = 0; i < a.size(); ++i )
v.push_back( F(a[i]) );
// but I need something like
std::vector<int>v( a.begin(), a.end(), <|applying each element to F|> );
Can I create container something like above not calling reserve explicitly and any reallocation?
EDIT:
I want avoid reserve; because othercase my first solution is good
for me :)
I want avoid any resize; because it's initialzed each
element by default ctor.
I will use this in the real project which may included many 3-rd party libraries ( boost, Soft-STL, ...).
The standard algorithm std::transform does exactly this!
std::vector<int> v(a.size());
std::transform(
std::begin(a), std::end(a),
std::begin(v),
F
);
You can start with an empty vector and use std::back_inserter, if you like:
std::vector<int> v;
std::transform(
std::begin(a), std::end(a),
std::back_inserter(v),
F
);
But you're subjecting yourself to needless re-allocations if you do that (unless you reserve first, as in your original attempt). You can decide for yourself what your priority is.
Use std::transform:
#include <algorithm> // std::transform
#include <iterator> // std::back_inserter
....
transform(a.begin(), a.end(), back_inserter(v), F);
You may want to call v.reserve(asize()) first to avoid re-allocations.
Another solution is to use boost::transform_iterator. The benefit is that you can pass iterators to the container constructor. That avoids memory reallocations compared to when using std::back_inserter or having to call reserve or resize on the destination. All in one statement:
std::vector<int> result(
boost::make_transform_iterator(std::begin(a), F)
, boost::make_transform_iterator(std::end(a), F)
);
You can achieve terser syntax though, like this:
std::vector<int> result(transform_range(a, F));
transform_range implementation:
template<class Iterator>
struct AutoSequence
{
Iterator const beg_, end_;
template<class T>
operator std::vector<T>() const {
return {beg_, end_};
}
};
template<class Function, class InSeq>
auto transform_range(InSeq const& in) -> AutoSequence<decltype(boost::make_transform_iterator<Function>(in.begin()))> {
return {
boost::make_transform_iterator<Function>(std::begin(in))
, boost::make_transform_iterator<Function>(std::end(in))
};
}
template<class Function, class InSeq>
auto transform_range(InSeq const& in, Function&& f) -> AutoSequence<decltype(boost::make_transform_iterator(in.begin(), f))> {
return {
boost::make_transform_iterator(std::begin(in), f)
, boost::make_transform_iterator(std::end(in), f)
};
}

unique_copy elements value from map to vector

I have a map<K, V> and I want to use unique_copy to put the values into a vector<V>.
I tried this but it doesn't work:
#include <iostream>
#include <map>
#include <algorithm>
#include <vector>
#include <functional>
using namespace std;
using namespace placeholders;
int main() {
map<std::string, int> mp;
mp["1"] = 1;
mp["2"] = 2;
mp["3"] = 3;
mp["4"] = 3;
mp["5"] = 3;
mp["6"] = 4;
mp["7"] = 2;
vector<int> vec;
unique_copy( mp.begin(), mp.end(),
back_inserter(vec),
bind(&map<std::string, int>::value_type::second, _1) );
for( auto& i : vec )
cout<< i <<" ";
}
Expected output:
1 2 3 4 2
All google searches return ways to use transform but I need unique_copy. Is there any way to make this work?
There's no way to do this directly, because the value type of the map iterator and the vector iterator are not compatible. You really need a transform of some sort, or an iterator adaptor.
You could use boost::transform_iterator for this:
auto getValue = [](const std::map<std::string, int>::value_type &pair) { return pair.second; };
unique_copy(
boost::make_transform_iterator(mp.begin(), getValue),
boost::make_transform_iterator(mp.end(), getValue),
back_inserter(vec)
);
If you cannot use Boost, you'll have to write such an iterator adaptor yourself:
template <class T_PairIterator>
struct SecondIterator
{
typedef T_PairIterator PairIterator;
typedef typename std::iterator_traits<PairIterator>::iterator_category iterator_category;
typedef typename std::iterator_traits<PairIterator>::value_type::second_type value_type;
typedef typename std::iterator_traits<PairIterator>::difference_type difference_type;
typedef value_type *pointer;
typedef value_type &reference;
PairIterator it;
SecondIterator() {}
explicit SecondIterator(PairIterator it) : it(it) {}
pointer operator-> () const { return &it->second; }
reference operator* () const { return it->second; }
SecondIterator& operator++ () { ++it; return *this; }
SecondIterator operator++ (int) { SecondIterator ret(*this); ++it; return ret; }
};
template <class T>
bool operator== (const SecondIterator<T> &lhs, const SecondIterator<T> &rhs)
{ return lhs.it == rhs.it; }
template <class T>
bool operator!= (const SecondIterator<T> &lhs, const SecondIterator<T> &rhs)
{ return !(lhs == rhs); }
template <class T>
SecondIterator<T> makeSecondIterator(const T &it)
{ return SecondIterator<T>(it); }
You could then use it like this:
unique_copy(
makeSecondIterator(mp.begin()),
makeSecondIterator(mp.end()),
back_inserter(vec)
);
Of course, the adaptor could be made a bit more generic (perhaps usable for first as well), and/or more encapsulated (it needn't be public); it would also need proper handling for const-iterators. But the above should be enough to give you the idea.
std::copy followed by vec.erase( std::unique( vec.begin(), vec.end() ), vec.end() ); will end up in the same place as unique_copy with a transform would. It may use extra memory in the middle (before removing redundant entries) but is pretty clean.
A manual copy is another option:
if(!src.empty()){
auto* prev = &src.begin()->second;
dest.push_back(*prev);
for(auto it = std::next(src.begin()); it != sec.end(); ++it){
if (it->second == *prev) continue;
prev=&it->second;
dest.push_back(*prev);
}
}
with the note thay prev is technically redundant (use dest.back() instead of *prev except when push_backing -- there use ->second).
As Vijay says in his comment, you could use a std::set instead of a vector (if you don't need the individual features of a vector, of course, like contiguous storage, custom ordering or random access). In this case the std::set::insert will already take care of removing duplicates and you can use std::transform in the way you tried:
std::set<int> set;
std::transform( mp.begin(), mp.end(), std::inserter(set),
bind(&std::map<std::string, int>::value_type::second, _1) );
As to your modified question
std::set won't insert the value if it is already in the set but I
don't want consecutive duplicates.
Well, in this case just do a normal copy (i.e. std::transform) into the vector and do a std::unique afterwards, which will just remove all consecutive duplicates:
std::transform( mp.begin(), mp.end(), std::inserter(vec),
bind(&std::map<std::string, int>::value_type::second, _1) );
vec.erase( std::unique(vec.begin(), vec.end()), vec.end() );

Getting a list of values from a map

Is there an stl way to get a list of values from a map?
i.e, I have:
std::map<A,B> myMap;
and I would like a function that will return just the list of values, i.e, std::list<B> (or set for that matter.
Is there a built-in stl way to do this?
A map element is defined as a map::value_type, and the type of it is a pair<A,B>. first is the key and second is the value. You can write a functor to extract second from a value_type, and copy that in to a vector (or a list, or whatever you want.) The best way to do the copying is to use transform, which does just what its name implies: it takes a value of one type and transforms it to a different type of value.
Here's a complete working example:
#include <cstdlib>
#include <map>
#include <string>
#include <algorithm>
#include <iterator>
#include <vector>
#include <iostream>
using namespace std;
typedef map<unsigned, string> MyMap;
MyMap my_map;
struct get_second : public std::unary_function<MyMap::value_type, string>
{
string operator()(const MyMap::value_type& value) const
{
return value.second;
}
};
int main()
{
my_map[1] = "one";
my_map[2] = "two";
my_map[3] = "three";
my_map[4] = "four";
my_map[5] = "five";
// get a vector of values
vector<string> my_vals;
transform(my_map.begin(), my_map.end(), back_inserter(my_vals), get_second() );
// dump the list
copy( my_vals.begin(), my_vals.end(), ostream_iterator<string>(cout, "\n"));
}
EDIT:
If you have a compiler that supports C++0x lambdas, you can eliminate the functor entirely. This is very useful for making code more readable and, arguable, easier to maintain since you don't end up with dozens of little one-off functors floating around in your codebase. Here's how you would change the code above to use a lambda:
transform(my_map.begin(), my_map.end(), back_inserter(my_vals), [](const MyMap::value_type& val){return val.second;} );
There's nothing built in, no. It's simple enough to write your own function, though: Iterate over the map. The iterator will give you a pair<A, B>. Add each second value to the result list.
You can't just "get" such a list because there is no pre-existing list stored anywhere in the guts, but you can build one:
typedef std::map<A,B> myMapType;
myMapType myMap;
std::list<B> valueList;
for (myMapType::const_iterator it=myMap.begin(); it!=myMap.end(); ++it) {
valueList.push_back( it->second );
}
Or if you really like the more STL way:
class GetSecond {
template<typename T1, typename T2>
const T2& operator()( const std::pair<T1,T2>& key_val ) const
{ return key_val.second; }
};
typedef std::map<A,B> myMapType;
myMapType myMap;
std::list<B> valueList;
std::transform(myMap.begin(), myMap.end(), std::back_inserter(valueList),
GetSecond());
One of many "built-in" ways is of course the most obvious one. Just iterate over all pair elements, which are ordered by key (pair::first), and add the value (pair::second) to a new container, which you can construct with the correct capacity to get rid of excess allocations during the iteration and adding.
Just a note: std::list is seldom the container you actually want to be using. Unless, of course, you really, really do need its specific features.
Sure.
std::list<B> list;
std::for_each(myMap.begin(), myMap.end(), [&](const std::pair<const A, B>& ref) {
list.push_back(ref.second);
});
If you don't have a C++0x compiler, first you have my sympathies, and second, you will need to build a quick function object for this purpose.
You can use boost's transform_iterator: http://www.boost.org/doc/libs/1_64_0/libs/iterator/doc/transform_iterator.html
struct GetSecond {
template <typename K, typename T>
const T& operator()(const std::pair<K, T> & p) const { return p.second; }
template <typename K, typename T>
T& operator()(std::pair<K, T> & p) const { return p.second; }
};
template <typename MapType>
auto begin_values(MapType& m) -> decltype(boost::make_transform_iterator(m.begin(), GetSecond())) {
return boost::make_transform_iterator(m.begin(), GetSecond());
}
template <typename MapType>
auto end_values(MapType& m) -> decltype(boost::make_transform_iterator(m.end(), GetSecond())) {
return boost::make_transform_iterator(m.end(), GetSecond());
}
template <typename MapType>
struct MapValues {
MapType & m;
MapValues(MapType & m) : m(m) {}
typedef decltype(begin_values(m)) iterator;
iterator begin() { return begin_values(m); }
iterator end() { return end_values(m); }
};
template <typename MapType>
MapValues<MapType> get_values(MapType & m) {
return MapValues<MapType>(m);
}
int main() {
std::map<int, double> m;
m[0] = 1.0;
m[10] = 2.0;
for (auto& x : get_values(m)) {
std::cout << x << ',';
x += 1;
}
std::cout << std::endl;
const std::map<int, double> mm = m;
for (auto& x : get_values(mm)) {
std::cout << x << ',';
}
std::cout << std::endl;
}

How to use BOOST_FOREACH with a boost::ptr_map?

How can I use BOOST_FOREACH efficiently (number-of-character/readability-wise) with a boost::ptr_map?
Kristo demonstrated in his answer that it is possible to use BOOST_FOREACH with a ptr_map, but it does not really save me any typing (or makes my code really more readable) than iterating over the ptr_map with an iterator:
typedef boost::ptr_container_detail::ref_pair<int, int* const> IntPair;
BOOST_FOREACH(IntPair p, mymap) {
int i = p.first;
}
// vs.
boost::ptr_map<int, T>::iterator it;
for (it = mymap.begin(); it != mymap.end(); ++it) {
// doSomething()
}
The following code is somewhere along the lines what I wish for. It follows the standard way on how to use BOOST_FOREACH with a std::map. Unfortunately this does not compile:
boost::ptr_map<int, T> mymap;
// insert something into mymap
// ...
typedef pair<int, T> IntTpair;
BOOST_FOREACH (IntTpair &p, mymap) {
int i = p.first;
}
As STL style containers, the pointer containers have a value_type typedef that you can use:
#include <boost/ptr_container/ptr_map.hpp>
#include <boost/foreach.hpp>
int main()
{
typedef boost::ptr_map<int, int> int_map;
int_map mymap;
BOOST_FOREACH(int_map::value_type p, mymap)
{
}
}
I find that using a typedef for the container makes the code a lot easier to write.
Also, you should try to avoid using the contents of detail namespaces in boost, it's a boost convention that they contain implementation details.
I just ran into the same problem today. Unfortunately, Daniel's suggestion will not work with a constant reference to a map. In my case, the ptr_map was a member of a class, and I wanted to loop through it in a const member function. Borrowing Daniel's example, this is what I had to do in my case:
#include "boost/ptr_container/ptr_map.hpp"
#include "boost/foreach.hpp"
int main()
{
typedef boost::ptr_map<int, int> int_map;
int_map mymap;
const int_map& mymap_const_ref(mymap);
BOOST_FOREACH(int_map::const_iterator::value_type p, mymap_const_ref)
{
}
}
It seems that int_map::const_iterator::value_type is equivalent to boost::ptr_container_detail::ref_pair<int, const int* const>.
Save yourself the typing and improve readability by using tuples:
boost::ptr_map<int, T> mymap;
int key;
T * value;
BOOST_FOREACH(boost::tie(key, value), mymap)
{
...
}
This example code compiled for me with g++ 4.1.2:
#include "boost/ptr_container/ptr_map.hpp"
#include "boost/foreach.hpp"
int main()
{
boost::ptr_map<int, int> mymap;
typedef boost::ptr_container_detail::ref_pair<int, int* const> IntPair;
BOOST_FOREACH(IntPair p, mymap)
{
int i = p.first;
}
return 0;
}
I use this homebrew template which adds an iteration type which can be handled by BOOST_FOREACH
namspace homebrew
{
template
<
class Key,
class T,
class Compare = std::less<Key>,
class CloneAllocator = boost::heap_clone_allocator,
class Allocator = std::allocator< std::pair<const Key,void*> >
>
class ptr_map :
public boost::ptr_map<Key,T,Compare,CloneAllocator,Allocator>
{
public:
typedef boost::ptr_container_detail::ref_pair<Key,const T* const> const_ref;
typedef boost::ptr_container_detail::ref_pair<Key,T* const> ref;
};
}
Let's assume that foo and bar are two of your favorite types ;)
typedef homebrew::ptr_map<foo,bar> Map;
int f( const Map& m )
{
BOOST_FOREACH(Map::const_ref v, m)
{
v.first; // foo
v.second; // const bar* const
}
}
or
int f( Map& m )
{
BOOST_FOREACH(Map::ref v, m)
{
v.first; // foo
v.second; // bar* const
}
}
Which one you have to use doesn't seem to depend on the way you use it in the loop (const or non-const) but on the map's constness!! So the following will end up in an error...
int f( Map& m )
{
BOOST_FOREACH(Map::const_ref v, m) // can't use const_ref because m isn't const
{
...
}
}
Weird! isn't it?
The greatest thing to me is that of all this solutions which were suggested here, this is the first one which is handled correctly by the Eclipse CDT syntax coloring (when using the 'Code/Problem' syntax coloring attribute).
It should compile without the reference:
BOOST_FOREACH (IntTpair p, mymap)
I think the problem is that maps do not actually store objects as pairs, but as a tree structure with the first element as the key, so BOOST_FOREACH can't get a reference to a pair but it can create a temporary copy of one.
using ::value_type won't let you const-iterate through the container. I use iterator reference types
typedef boost::ptr_map< myKey, myPtrType > MyMap;
MyMap m;
BOOST_FOREACH( MyMap::iterator::reference it, m )
do_something( it.second );
BOOST_FOREACH( MyMap::const_iterator::reference it, m )
do_something_const( it.second );
In the end, I went for declaring the iteration variable before the loop.
std::pair<std::string, TrailStep*> step;
BOOST_FOREACH(step, m_steps)
{
SAFE_DELETE(step.second);
}
But indeed, there ought to be a simpler way. (Use D instead?)
You might try this uber-cool way to iterate over maps, ptr or otherwise:
https://svn.boost.org/trac/boost/attachment/ticket/3469/foreach_field.hpp
// no typedef needed
BOOST_FOREACH_FIELD((int i)(const T& t), mymap)
// do something with i or t instead of first/second
I'm not sure it will work with a template parameter, but maybe you used that just for abstraction.