STL map to STL vector

L

Luca Risolia

Mike said:
Is it possible to simply (single statement) move the data from one
container type (the TeamMap, below) to a different type of container
(the TeamVector, below)?

std::for_each(std::make_move_iterator(std::begin(TeamMap)),
std::make_move_iterator(std::end(TeamMap)),
[](decltype(TeamMap)::value_type&& v) {
TeamVector.push_back(std::move(v.second));
});
 
M

Mike Copeland

Is it possible to simply (single statement) move the data from one
container type (the TeamMap, below) to a different type of container
(the TeamVector, below)? My data resides in the map object, but since I
have to occasionally display the data in different orders (e.g.
teamName, teamTypeCode), I must copy the data to a vector and sort it
before producing my listings.
I know that I can copy the individual objects from the map to the
vector one-at-a-time, but I'm hoping that there's a technique that will
do it simply and quickly. Any thoughts? TIA


typedef map<char, int> ShirtStats;
struct TeamData
{
bool isAdded; // Added to container data
bool isValidTeam; // really is a team
char teamTypeCode; // Team type Code
int teamMembers1; // Count of Team Members-1
int teamMembers2; // Count of Team Members-2
int genderCounts[NMAXEVT][2]; // counts of Gender by Event
int shirtTotalMatrix[5][8];
string teamCode; // Team's Code (strTId)
string teamName; // Team's Name
ShirtStats shirtStats;
} extern teamWork;
typedef map<string, TeamData> TeamMap;
TeamMap::iterator tIter;
ShirtStats::iterator ssIter;
TeamMap teamMap;
typedef vector<TeamData> TeamVector;
typedef TeamVector::iterator TeamIter;
TeamVector teamVect;
TeamIter tvIter;
 
I

Ian Collins

Mike said:
Is it possible to simply (single statement) move the data from one
container type (the TeamMap, below) to a different type of container
(the TeamVector, below)? My data resides in the map object, but since I
have to occasionally display the data in different orders (e.g.
teamName, teamTypeCode), I must copy the data to a vector and sort it
before producing my listings.
I know that I can copy the individual objects from the map to the
vector one-at-a-time, but I'm hoping that there's a technique that will
do it simply and quickly. Any thoughts? TIA

Construct the vector using the map's beginning and end:

TeamVector t( teamMap.begin(), teamMap.end() );
 
W

woodbrian77

Is it possible to simply (single statement) move the data from one
container type (the TeamMap, below) to a different type of container

(the TeamVector, below)? My data resides in the map object, but since I
have to occasionally display the data in different orders (e.g.
teamName, teamTypeCode), I must copy the data to a vector and sort it
before producing my listings.
I know that I can copy the individual objects from the map to the
vector one-at-a-time, but I'm hoping that there's a technique that will
do it simply and quickly. Any thoughts? TIA

How about

http://www.boost.org/doc/libs/1_55_0/libs/multi_index/doc/index.html


That would help you avoid the copying.

Brian
Ebenezer Enterprises - In G-d we trust.
http://webEbenezer.net
 
I

Ike Naar

Construct the vector using the map's beginning and end:

TeamVector t( teamMap.begin(), teamMap.end() );

That won't work because the value type of the map is not the same as the
value type of the vector.
 
I

Ike Naar

Mike said:
Is it possible to simply (single statement) move the data from one
container type (the TeamMap, below) to a different type of container
(the TeamVector, below)?

std::for_each(std::make_move_iterator(std::begin(TeamMap)),
std::make_move_iterator(std::end(TeamMap)),
[](decltype(TeamMap)::value_type&& v) {
TeamVector.push_back(std::move(v.second));
});

From Mike's description (snipped) it looks like he wants to copy the
data, not move it.
 
I

Ian Collins

Ike said:
That won't work because the value type of the map is not the same as the
value type of the vector.

Whoops, you're right. I was thinking of std::set, not map.
 
A

Alf P. Steinbach

Whoops, you're right. I was thinking of std::set, not map.

The following little class comes in handy then:


Code:
template< class Iter, class Referent, class Remap_func >
struct Remap_iter
: Iter
{
typedef Iter Base;

Referent& operator* ()
{ return Remap_func()( Base::operator*() ); }

Referent const& operator* () const
{ return Remap_func()( Base::operator*() ); }

Remap_iter( Iter const& it )
: Base( it )
{}
};
[code]


It doesn't cover all the cases but one can just add to it. Good enough
for Mike's code with Visual C++. That is,


[code]
struct Map_to_vec
{
TeamData& operator()( TeamMap::value_type& v )
{ return v.second; }
};

int main()
{
typedef Remap_iter< TeamMap::iterator, TeamData, Map_to_vec > Vec_iter;

TeamMap         m;
TeamVector      v( Vec_iter( m.begin() ), Vec_iter( m.end() ) );
}


compiles nicely.

Disclaimer: haven't tested it with any data!


Cheers & hth.,

- Alf
 
A

Alf P. Steinbach

Or instead you could do the sensible thing and use a 1-liner:

for(auto& element: theMap) theVector.push_back(element.second);

I'm not sure it's always so sensible as a /general/ solution, which I
somewhat unthinkingly aimed at.

In particular, if you want to declare `theVector` as `const`, which is
usually a good idea since one can then be sure at a glance that it won't
change, then the above approach needs a lambda or named function:

vector<TeamData> theVector( [&]() -> vector<TeamData>
{
vector<TeamData> result;
for(auto& element: m) theVector.push_back(element.second);
return result;
}() );

Like, ouch. I think the "iterator conversion" approach stands a much
better chance of reducing that to something more simple &
straightforward, something without case-specific code. Perhaps even
something readable...

However, I agree that the loop is probably the best advice to Mike, the OP.

Dang, I didn't think of that.


Cheers,

- Alf
 
S

Seungbeom Kim

Mike said:
Is it possible to simply (single statement) move the data from one
container type (the TeamMap, below) to a different type of container
(the TeamVector, below)?

std::for_each(std::make_move_iterator(std::begin(TeamMap)),
std::make_move_iterator(std::end(TeamMap)),
[](decltype(TeamMap)::value_type&& v) {
TeamVector.push_back(std::move(v.second));
});

(Assuming you want to move the contents)

Given the following declarations:
struct TeamData;
typedef std::map<std::string, TeamData> TeamMap; TeamMap teamMap;
typedef std::vector<TeamData> TeamVector; TeamVector teamVect;

It should read like this:

std::for_each(std::make_move_iterator(std::begin(teamMap)),
std::make_move_iterator(std::end(teamMap)),
[&](decltype(teamMap)::value_type&& v) {
teamVect.push_back(std::move(v.second));
});

By the way, I think it can be made simpler like this:

std::for_each(std::begin(teamMap),
std::end(teamMap),
[&](decltype(teamMap)::value_type& v) {
teamVect.push_back(std::move(v.second));
});

A function that takes an lvalue parameter and moves things out of it
is dangerous in general, but this is a lambda function used specifically
for moving the contents out of an existing container, so it is okay.
And whether you take an lvalue parameter or an rvalue parameter,
it becomes an lvalue inside the function and you need std::move anyway.
 
J

Jorgen Grahn

Maps may be the worst standard container.

Why? I find it invaluable -- it gives you the type->type mapping most
modern languages have. (Of course, unordered_map does most of the
same things, and for the few cases where it matters, slightly faster.)

/Jorgen
 
W

woodbrian77

Why? I find it invaluable -- it gives you the type->type mapping most
modern languages have. (Of course, unordered_map does most of the
same things, and for the few cases where it matters, slightly faster.)

I think the pair based interface is flawed and there should
instead be a container like Boost.intrusive.rbtree in the
standard. It wouldn't have to be intrusive, but it
wouldn't be pair based. Then you're able to work with one
type rather than two types. No more "first' and "second".
Multi_index doesn't use pair in its interface either.

I don't know if what Leigh wrote is correct, but if it is
that would be another strike against maps.

Maps keep track of the number of elements in them. The
boost intrusive library gives you the option to not
have to pay for keeping track of the elements like that.

I think unordered_map is a lame name.

Brian
Ebenezer Enterprises - Heavenly code.
http://webEbenezer.net
 
B

blaz.bratanic

Or instead you could do the sensible thing and use a 1-liner:



for(auto& element: theMap) theVector.push_back(element.second);



--- news://freenews.netfront.net/ - complaints: (e-mail address removed) ---


std::transform(team_map.begin(), team_map.end(), std::back_inserter(team_vec),
[](decltype(team_map)::value_type e) { return e.second; });

One liner is indeed much clearer :)
 
Ö

Öö Tiib

I think the pair based interface is flawed and there should
instead be a container like Boost.intrusive.rbtree in the
standard.

That tree is not map. Map consists of relations of value
of some type to other value of (possibly some other) type.
Such relations or "mappings" are the point of map. You
can use tree of mappings as map but then you need to
describe what is mapping.
It wouldn't have to be intrusive, but it
wouldn't be pair based. Then you're able to work with one
type rather than two types. No more "first' and "second".
Multi_index doesn't use pair in its interface either.

Pair is a way to represent a mapping. There may be
endless other ways but notice that you propose none.

If you need a set with specific iteration order or search
criteria then you indeed can use 'boost::intrusive::set' or
'boost::intrusive::multiset'. If you need a set with
several iteration orders or search criteria then you
can use 'boost::multi_index_container'. Those are not
maps. Do not use 'map', 'multimap' or 'unordered_map'
indeed if you just need 'set'.
I don't know if what Leigh wrote is correct, but if it is
that would be another strike against maps.

Yes, it is correct about most implementations of vector.
Those reserve automatically double bigger buffer when
vector runs out of space. So benefit is log N and so
the difference diminishes when N grows. If you want
a map implementation based on vector then take
'boost::flat_' (multi)map/set'.
Maps keep track of the number of elements in them. The
boost intrusive library gives you the option to not
have to pay for keeping track of the elements like that.

Saving one integer value is questionable here. It is most
likely that removing it is inefficient like with C string.
C string is less efficient than 'std::string' on most of cases
precisely because of not storing the length anywhere.
So you need to show that you "pay", otherwise it might
be empty claim.
I think unordered_map is a lame name.

There is 'typedef' if you need better describing alias for type.
Therefore "is a lame name" is not that convincing argument.
 
W

woodbrian77

It almost sounds like you are talking about std::set.

For most situations where you would use std::map, you can use std::set
instead, but to get the same functionality you have to design your
element type appropriately. std::map simply saves you the trouble
and gives you a handy shortcut when you need a type->type relational
data container.

I believe std::set has some const assumption that makes it
difficult to use as an alternative.
It's descriptive. What else do you want?

It isn't very descriptive. It's like saying a
marble is not blue. Hash_map would be better.
I think there was some history with that name,
but think the name could still be changed to
hash_map.

Brian
Ebenezer Enterprises
http://webEbenezer.net
 
Ö

Öö Tiib

It isn't very descriptive. It's like saying a
marble is not blue. Hash_map would be better.

Why? "Hash" sounds like Klingon/Orcish word
while every child understands "unordered". On
most cases the fact that unordered map is
built internally on hash table is as unimportant
as the fact that set is built upon red-black tree.
I think there was some history with that name,
but think the name could still be changed to
hash_map.

When the requirements of standard are sufficiently
different than those of prior art then it is good idea
to rename. Same was with 'boost::scoped_ptr' that
became 'std::unique_ptr'.
 
W

woodbrian77

Why? "Hash" sounds like Klingon/Orcish word
while every child understands "unordered". On
most cases the fact that unordered map is
built internally on hash table is as unimportant
as the fact that set is built upon red-black tree.

I believe unordered_map has to have a hash function.
It defaults to std::hash if you don't supply your own.
The Microsoft documentation on unordered_map says the
sequence is "weakly ordered". That's a more accurate
description than "unordered". But I don't like
"weakly_ordered_map" for a name either. "Hash_map"
is nice because it isn't so long.

When the requirements of standard are sufficiently
different than those of prior art then it is good idea
to rename.

What different requirements?

Brian
Ebenezer Enterprises
http://webEbenezer.net
 
W

woodbrian77

what I said about 'reserve' applied to vectors not maps; map doesn't
have a 'reserve'.

Oops. Thanks. I vaguely remember hearing discussion
about adding reserve functions to some other containers,
but I don't know if anything came of that.

Brian
Ebenezer Enterprises
http://webEbenezer.net
 
W

woodbrian77

Saving one integer value is questionable here. It is most
likely that removing it is inefficient like with C string.
C string is less efficient than 'std::string' on most of cases
precisely because of not storing the length anywhere.
So you need to show that you "pay", otherwise it might
be empty claim.

Some containers you rarely/never need to know their size.


Brian
Ebenezer Enterprises
http://webEbenezer.net
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top