• Advertisement

Archived

This topic is now archived and is closed to further replies.

std::map with overloaded operator [ ]?

This topic is 5712 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Let''s say I have
std::map<std::string,std::string> amap;
...
str = amap["ID_ABC"];
 
...so consider I know the n-th position of "ID_ABC", say 1, may I do this:
str = amap[1]; 
If cannot, which is the best way? Wrap the std::map and provide an operator[int] ? OR Overload the operator[] on the map<string,string> ? (which I think may not a good way) Any suggestion?

Share this post


Link to post
Share on other sites
Advertisement
A map does not guarantee that its data is stored in contiguous fashion, so you can''t "offset" into it. Wrapping it isn''t efficient either, as your wrapper will end up either having to iterate or maintain an internal conversion table. If you wanted integer lookup, you should have declared the map with an integer key type.

If you''re concerned about speed, take a look at std::hash_map (SGI and STLPort provide this; Dinkum doesn''t with MSVC6 though MSVC.Net has them).

Share this post


Link to post
Share on other sites
if you use a std::map and declare it

using namespace std;

map<string,string>

you cannot access the [] operator with anything other than a string.

It has to do with the way maps are implemented. As red-black b-trees, they aren''t necessarily stored in order. Indeed, the arrangement of the tree shifts, sometimes dramatically, on every insertion or deletion.

You really shouldn''t access an element of a map in any other way than with the key type that the map is declared with.

if you have a map<int,string> you can treat is as a sparse array, doing things like

my_map[0] = "blah";
my_map[100] = "200";

takes only 2 elements worth of memory, whereas with a vector, you''d be allocating 100 elements worth of mem.

but if you do a map<string,string>, don''t try to access it like my_map[0] or my_map[100]

Share this post


Link to post
Share on other sites
thanks guys.
Actually, my office is using MSVC5 ! So I assume that it is too old to actually compile STLPort flawlessly.

dearid, you gave me an idea... creating a class with two maps, one is <int,string>, another is map<string,string>... and overload operator[int] and operator[string].... since speed is not the concern over here.... any more tips/hints/suggestions are welcomed!

... oh man... GameDev.Net forums are damn helpful!

Share this post


Link to post
Share on other sites
quote:
Original post by DerekSaw
Actually, my office is using MSVC5 ! So I assume that it is too old to actually compile STLPort flawlessly.

I''m not so sure about that. MSVC5 was the first version to provide the basic functionality necessary to compile the STL without platform-specific hacks, IIRC. Check the STLPort website for more info.

quote:

daerid, you gave me an idea... creating a class with two maps, one is map<int,string>, another is map<string,string>... and overload operator[int] and operator[string].... since speed is not the concern over here.... any more tips/hints/suggestions are welcomed!

The real question is what are you trying to achieve? There may be much better ways to accomplish whatever you have in mind.

Share this post


Link to post
Share on other sites
I''m trying to simulate a row (or record) in a database table. e.g.

userid = record["user_id"]; // obtaining the field named "user_id"
// OR
userid = record[0]; // obtaining the 1st field




p/s: i''m storing all the fields'' data as strings.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Í don''t remember this


struct key_type
{
key_type():cmd_int(false) {}
key_type(const std::string s):cmd_int(false),str(s) {}
key_type(int i):cmd_int(true), idx(i) {}
key_type(const std::string s, int i):cmd_int(false),str(s),idx(i) {}


bool cmd_int;
std::string str;
int idx;
bool operator < (const key_type & k) const
{
if(cmd_int || k.cmd_int)
return idx < k.idx;
return str < k.str;
}
}


This should work, I''ve not tested, since operator[] takes a const reference to the key type and this allows implicit construction of a temporary using the single param constructor.

Share this post


Link to post
Share on other sites
AP:
That still doesn''t provide natural semantics for operator []; one can''t directly use a string or integer as index.

quote:
Original post by DerekSaw
I''m trying to simulate a row (or record) in a database table.
<example omitted>


class Record
{
public:
Record();
Record( const Record & r );
Record( std::vector< std::string > & column_headings );
 
bool InsertColumn( const std::string & heading, const int index );
const std::string & operator [] ( int index ) const;
const std::string & operator [] ( const std::string & heading ) const;
 
private:
std::map< std::string, std::string > m_columns;
std::vector< std::string > m_headings;
};

m_headings would contain the column headings in the order in which you would like them referenced. m_columns would contain the actual data, associated with the string headings (remember, maps have no concept of "ordering").

const std::string & Record::operator [] ( int index ) const
{
if( index < m_headings.size() )
return m_columns[ m_headings[index] ];
 
return "Out of bounds";
}
 
const std:;string & Record::operator [] ( const std::string & heading ) const
{
static std::map< std::string, std::string >::iterator iter = 0;
 
iter = m_columns.find( heading );
if( iter == m_columns.end() )
return "Invalid heading";
 
return *iter;
}

Using these two version of operator [], you can now obtain record data with natural syntax using both integers and strings as indices.

Note the three constructors (default, copy and initializer). The third version takes a vector of headings in the order you want them to be referenced when accessed by integer index. After initializing your record, the order of columns must be preserved (or operations will quickly turn to mush), so we provide the InsertColumn method:

bool Record::InsertColumn( const std::string & heading, const int index )
{
if( m_columns.find( heading ) == m_columns.end() )
{
m_headings.insert( index + 1, heading );
m_columns[ heading ] = "";
return true;
}
 
return false;
}

Enjoy!

Share this post


Link to post
Share on other sites
That was great.. with just little tweaking... and I''ll be enjoying it !

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
are you just looping through everything in the map? Look up iterators!

Share this post


Link to post
Share on other sites
The only problem with Oluseyi''s solution is that map::operator [ ] is not a const function. You''ll have to make the maps mutable (which just makes me feel all dirty inside), or use map::find instead of map::operator [ ].

If you don''t want to construct two maps and worry about inserting columns, you can always use the std::distance and std::advance algorithms in the aptly-named header algorithm. However, with maps, these are both O(N) operations, whereas Oluseyi has given you a solution that''s O(logN).

Share this post


Link to post
Share on other sites
quote:
Original post by Stoffel
The only problem with Oluseyi''s solution is that map::operator [ ] is not a const function.

You''re right. In Record::operator [] ( int index ) I should have used map::find instead of map::operator []. That''s the only non-conforming case I can see.

Share this post


Link to post
Share on other sites
quote:
Original post by Anonymous Poster
are you just looping through everything in the map? Look up iterators!


Not necessary. But isn''t that using Oluseyi method is a ''lil faster than iterating the map.

Share this post


Link to post
Share on other sites
Loki (Modern C++ Design) has a data structure called AssocVector which is a map with constant time access, but slow for inserting/deletion.

Share this post


Link to post
Share on other sites
quote:

...
m_headings.insert( index + 1, heading );
...



I got error here:
error C2664: 'class CString *std::vector<...>::insert(class CString *,const class CString &)' : cannot convert parameter 1 from 'int' to 'class CString *'

I thought it should be:
std::vector::insert(int,const CString&)  

instead of
std::vector::insert(CString*, const CString&)  


[edited by - DerekSaw on July 1, 2002 12:06:41 AM]

Share this post


Link to post
Share on other sites
quote:
Original post by DerekSaw
I got error here:
error C2664: ''class CString *std::vector<...>::insert(class CString *,const class CString &)'' : cannot convert parameter 1 from ''int'' to ''class CString *''


My mistake. std:;vector::insert takes an iterator as the first argument. Change the line to
m_headings.insert( &m_headings[index + 1], heading ); 

and everything should be just fine.

Share this post


Link to post
Share on other sites
DerekSaw:
I''d also advise using std::string instead of CString (is that your own version or the MFC version?) as the former is consistent with the rest of the STL, which can be a major advantage. You can also apply STL algorithms to std::strings easily but might need to jump through a few hoops to use them with CString (and they might still break).

Share this post


Link to post
Share on other sites
..I would hope everything was in STL and Win32 API... too bad the project I''m on need MFC (not my decision anyway) and lot''s of CDatabase/CRecordset.

Anyway, thanks for your advice.

One thing I found out that.. this don''t always work.

typedef std::map<...> SomeMap;
SomeMap::iterator SomeFunc(...)
{
...
return NULL; // i.e. some error in search/find
..
}

For MSVC, it is fine. For BCC551, I can''t, and need to return somemap.end().

Share this post


Link to post
Share on other sites
quote:
Original post by DerekSaw
..I would hope everything was in STL and Win32 API... too bad the project I''m on need MFC (not my decision anyway) and lot''s of CDatabase/CRecordset.



Aaahh but have you checked this ?

Documents [ GDNet | MSDN | STL | OpenGL | Formats | RTFM | Asking Smart Questions ]
C++ Stuff [ MinGW | Loki | SDL | Boost. | STLport | FLTK | ACCU Recommended Books ]

Share this post


Link to post
Share on other sites
Yep, I knew that quite some time ago. But I don't need 'so much'. The project itself already span across 300+ cpp/h files, not including others COMs' source... and the most fustrated thing is that it was previously written & modified by many programmers, going thru them is just like 'eating' Power Word: Confuse with no saving throws.

Thanx anyway.

[edited by - DerekSaw on July 3, 2002 8:11:05 AM]

Share this post


Link to post
Share on other sites

  • Advertisement