Overloaded [] operator for hash table to a vector - c++

I am working on a simple hash table in C++. I have methods to insert, delete, and search the hash table for the specified key. I know that the C++ map STL container can handle my situation, but I would kind of like to code my own as an educational exercise.
Basically I have a hash table that will take a single string and map it to a vector of other strings. This is easy to do in a method because calling a .Add() or .Delete() will behave as expected. I would however like to create an overloaded [] operator to the class that is able to do these operations on the vector.
For example, if I want to add an item to the vector I can write something like this:
hashTable[string1] = newString;
This will set the new string as a member of my vector. The same can be said for delete and search.
hashTable[string1] = "";
cout << hashTable[string1] << endl;
My major problem is that I do not know how to overload the [] operator to gain this functionality. I have this function coded up right now. It works on a basic 1 to 1 string match, but not on a string to vector match.
//Return a reference to a vector to update then reassign?
vector& HashClass::operator[](const string index)
{
assert(size >= 0 && size < maxSize);
Hash(key);
return hashTable[index];
}
I think I'm most stuck on the idea of having a vector return that later needs to be assigned. As the user, I would find this kludgy.

This question is closely related to another question: what behavior do
you want when you access a non-existant value other than in an
assignment? In other words, what do you want to happen when you write:
std::cout << hashTable[string] << std::endl;
and string is not present in the table?
There are two possible approaches: you can consider it an error, and
throw an exception, or abort, or something similar; or you can return
some sort of default, built with the default constructor, or provided by
the client earlier.
The standard map and unordered_map take the second approach, using the
default constructor to construct a new value. This allows a very simple
solution: if operator[] isn't present, you insert it, initializing it
with the default value. Then you return a reference to it;
hashTable[string] = newString; assigns through the reference to an
already existing value.
In many use cases, the first approach will be preferable (perhaps with a
contains function, so you can test up front whether the operator[]
will find something or not). To implement the first approach, you must
first implement specific functions for each type of access:
template <typename Key, typename Value>
class HashTable
{
public:
Value* get( Key const& key ) const;
void set( Key const& key, Value const& value );
};
(I generally make these public; there's no reason to forbid their use by
a client.)
Then, you define operator[] to return a proxy, as follows:
template <typename Key, typename Value>
class HashTable
{
public:
class Proxy
{
HashTable* myOwner;
Key myKey;
public:
Proxy( HashTable* owner, Key const& key )
: myOwner( owner )
, myKey( key )
{
}
operator Value const&() const
{
Value const* result = myOwner->get( myKey );
if ( result == NULL ) {
// Desired error behavior here...
}
return *result;
}
Proxy const& operator==( Value const& value ) const
{
myOwner->set( myKey, value );
return *this;
}
};
Value* get( Key const& key ) const;
void set( Key const& key, Value const& value );
Proxy operator[]( Key const& key )
{
return Proxy( this, key );
}
};
Thus, when you write:
hashTable[key] = newString;
, the proxy's operator= will call hashTable.put( key, newString );
in other contexts, however, it will call the implicit type conversion on
the proxy, which calls hashTable.get( key ).
In some cases, even if you desire to return a default value, it may be
preferable to use this solution: the get function is not required to
insert anything into the hash table, so the table doesn't fill up with
all of the misses, and you can overload the operator[] on const, so
you can use it on a const hash table as well. Also, it doesn't
require the value type to have a default constructor.
It does have one disadvantage with respect to the solution used in the
standard; since you can't overload operator., you can't make the proxy
behave like a reference, and things like:
hashTable[string].someFunction();
don't work. A work-around is to overload operator-> in the proxy, but
this leads to a somewhat unnatural syntax:
hashTable[string]->someFunction(); // But the hash table contains
// values, not pointers!!!
(Don't be mislead by the implicit conversion to a reference. An
implicit conversion will not be considered for a in an expression
a.b.)

In C++, [] access to associative containers is generally given the semantics of default-constructing an object of the mapped type, inserting it with the key, and returning a reference to the inserted mapped object.
So your operator[] would be implemented as:
string& HashClass::operator[](const string index)
{
assert(size >= 0 && size < maxSize);
Hash(key);
vector &v = hashTable[index];
if (index in v) {
...
} else {
v.push_back(string());
return v.back();
}
}

Related

Guarantees about key uniqueness in std::unordered_multimap

I was wondering about uniqueness of a key object inside an std::unordered_multimap when dealing with iteration.
I'll try to explain the point: I need to associate some data with the key type in the map, this data should not be considered in Hash or KeyEqual elements, but I need it to avoid storing a separate map with it (for optimization purposes).
So the code associated with my idea is the following:
struct Key {
void* data;
mutable bool attribute;
Key(void* data) : data(data), attribute(false) { }
bool operator==(const Key& other) const {
return data == other.data;
}
};
struct KeyHash {
size_t operator()(const Key& key) const {
return std::hash<void*>()(key.data);
}
};
class Foo {
public:
int i;
Foo(int i) : i(i) { }
};
std::unordered_multimap<Key, Foo, KeyHash> map;
The problem arises from the fact that although this works fine, there are no guarantees about the fact that the key retrieved as the first element of the std::pair<const Key, Foo> mapping to a single element is always the same. Being a pair of const Key it sounds like that every element in the map has its copy of the key by value, so that if I do
void* target = new int();
map.emplace(std::make_pair(target, Foo(1)));
map.emplace(std::make_pair(target, Foo(2)));
auto pit = map.equal_range(target);
pit.first->first.attribute = true;
std::cout << std::boolalpha << (++pit.first)->first.attribute << endl;
This yields false which is confirming what I was thinking. So there is indeed a lot of space wasted to store keys if you have multiple values with the same key (which is what you want since you are using an std::unordered_map).
I don't see any other solution rather than something like
struct Value
{
std::vector<Foo> foos;
bool attribute;
};
std::unordered_map<void*, Value> map;
Which allows me to pair the attribute with the key but makes everything less clean since it requires working with two levels of iterators.
Are there other solutions I'm not seeing?
23.5.5.1 Class template unordered_multimap overview [unord.multimap.overview]
1 An unordered_multimap is an unordered associative container that supports equivalent keys (an instance of
unordered_multimap may contain multiple copies of each key value) and that associates values of another
type mapped_type with the keys. The unordered_multimap class supports forward iterators.
An unordered_multimap may contain multiple copies of the key, if you would like a single copy of the key then potentially a unordered_map<K, vector<V>> might be more appropriate.

C++ array subscription

I'm having a problem getting the desired behaviour with array subscription and assignment.
Is there any way to determine whether assignment is used with array subscription?
EDIT
My question probably should have been, can I map [] to a getter, and []= to a setter
// Expect this to return a reference to the value if the key exists,
// or throw an exception if not
myMap["Key"];
// Expect this to always return a reference to the value
// so the value can be populated
myMap["Key"] = "Value";
// The method being used
template <typename K, typename V>
V& MyMap<K, V>::operator[](const K &key)
{
if(this->keyExists(key))
{
return this->find(key);
}
else
{
// At this point I'd like to throw an exception if
// assignment is not being used
this->insert(key, NULL);
return this->pairs[this->itemsStored].val;
}
};
Is there any way to determine whether assignment is used with array
subscription?
Simply, no.
When you do:
myMap["Key"] = "Value";
You're calling two member functions: first, map::operator[] and then -- on a totally different class -- key::operator=. When you simply do myMap["Key"] without the assignment nothing has changed with regards to how you interface with the map. The only difference is what you do next.
You could, I suppose, find some technical hack (like providing a const and non-const version that do different things) which will provide the behavior you are trying to achieve -- but it will be at the cost of poor design. Since you have perscribed within the non-const version that a missing key will be added, subsequently throwing in the non-const version is a major difference. This will be a nightmare to maintain. You will have very strange bugs arise when one version is actually being called when you expected the other to be called. People using your code will be confused and curse your name. Don't do it.
Instead, I suggest you're barking up the entirely wrong tree to begin with. Instead of trying to use operator[] const to determine the existence of a key, why not simply provide a member function that does simply that?
You can, if you wish, have this function throw if the key doesn't exist or simply return a bool.
There's only one way to be sure that no operator= will be called after operator[] (by overloading an operator[] const function), but that wouldn't work every time.
As there's no (easy) way to be able to know when an operator[] is being called with the operator= right after, I'd suggest you to follow the example of std::map by providing two different functions:
operator[], which always return a reference to the object; if the object does not exists, it is created
at, which returns a reference only if the object is already there, otherwise it throws an exception of type std::out_of_range
Yes. Just have the operator[] return a proxy. Something like
the following should work. (I'm using std::map for the
implementation; you can map it to whatever you're using)
template <typename KeyType, typename MappedType>
class MyMap
{
std::map<KeyType, MappedType> myImpl;
// ...
public:
void set( KeyType const& key, MappedType const& value )
{
myImpl[key] = value;
}
MappedType get( KeyType const& key )
{
auto entry = myImpl.find( key );
if ( entry == myImpl.end() ) {
throw DesiredException();
}
return entry->second;
}
class Proxy
{
MyMap* myOwner;
Key myKey;
publc:
Proxy( MyMap& owner, Key const& key )
: myOwner( &owner )
, myKey( key )
{
}
Proxy const& operator=( MappedType const& value ) const
{
myOwner->set( myKey, value );
return *this;
}
operator MappedType() const
{
return myOwner->get( myKey );
}
};
Proxy operator[]( KeyType const& key )
{
return Proxy( *this, key );
}
MappedType operator[]( KeyType const& key ) const
{
return get( key );
}
};
I'm not sure that this is a good idea, however. In general,
having a get( KeyType ) which returns a pointer to the mapped
element, or a null pointer if it isn't present, seems more
natural in C++.
DISCLAIMER : what follows is bad practice, and I do not recommend actually using this. It's merely here to show that what the OP wants is technically possible, even though it's a really bad idea (for reasons I'll go into further).
The bad idea
If you don't mind a bit of hassle, you can have a const and a non-const version of operator[] that behave differently. The const version would throw an exception when accessing a non-existent item, while the non-const version would default-construct a new item in that case.
As a proof of concept :
#include <iostream>
#include <stdexcept>
class Map {
public :
int value;
Map() { value = 42; }
const int& operator[](const size_t& pos) const { if (pos == 0) return value; else throw std::runtime_error("oops"); }
int& operator[](const size_t& pos) { return value; }
};
void showValue(const Map& myMap, size_t pos) {
try {
std::cout << "myMap[" << pos << "] = " << myMap[pos] << std::endl;
}
catch (std::runtime_error e) {
std::cout << "exception when accessing myMap[" << pos << "] : " << e.what() << std::endl;
}
}
int main(void) {
Map myMap;
showValue(myMap, 0);
showValue(myMap, 1);
myMap[0] = 5;
showValue(myMap, 0);
myMap[1] = 10;
showValue(myMap, 0);
return 0;
}
would print :
myMap[0] = 42
exception when accessing myMap[1] : oops
myMap[0] = 5
myMap[0] = 10
But the hassle I mentioned earlier, is to make sure the const version is used whenever the result won't be modified (in the example above, that's done by using a const reference).
Why it's bad
As mentioned at the beginning (and as pointed out by John Dibling in comments), this approach is not recommended. The problem is that :
it's difficult to know which version of operator[] will be called (in those cases where both can be called)
the two versions of operator[] behave differently (one throws an exception while the other would add a new item when called with the same argument)
Combine these two observations, and you get close to unpredictable behaviour, which will hurt you when you least expect it (trust me). And worse, it might be difficult to track down and fix such issues when they occur.
As a rule of thumb, the const and non-const versions of any member function should not differ in their core functionality. Violate that rule, and you invite the wrath of whoever has to maintain the code (and that probably includes your future self).
Any alternatives ?
So, don't do this. Instead, just either do it the same way std::map does (or better yet, just use std::map), or have a contains function you can call to check if an item exists.

Setter and Getter method for map

string var;
void setvar(string ivar)
{
var=ivar;
}
string getVar() const
{
return var;
}
as same way how can i write setter and getter method for a map like this
std::map varmap;
You can write a getter or setter for a field that's a std::map just as you would any other field - just have the getter return a std::map and have the setter accept a std::map.
Of course, if you have a field that's a std::map that you're trying to use getters and setters on, that might suggest that there's a better way to structure the program. Can you provide more details about what you're trying to do?
EDIT: The above answer is for a slightly different question than the one you asked. It seems like what you're interested in is
Given a class with a std::map as a data member, write a function to set a given key/value pair and a function to return the value associated with a given key.
The setter logic for this is not too hard - you just write a function that takes in the key and value and associates the key with the value. For example:
void put(const string& key, const string& value) {
varmap[key] = value;
}
Writing a getter is trickier because there's no guarantee that there's a value associated with a particular key. When this happens, you have multiple options.
You could return a sentinel value. For example, you might return an empty string if the given value isn't stored in the map anywhere. This makes the code for using the function easier to read, but risks using an invalid value in code.
You could throw an exception. This would be good if it represents a serious error for the given value not to exist. This has the drawback that if you look up a value, you always need to try/catch the logic to avoid propagation of errors.
You could associate a default value with the key, then hand that back. If you're writing a program that represents a music library, for example, you might hand back "(none)" or "(unknown)" if you tried to look up the artist for a song on which you have no data, for example.
No one of these approaches works best, and you'll need to think over which is most appropriate to your particular circumstance.
Entries in a std::map<Key, Value> must have a key and a value. The normal way of getting and setting them is:
my_map[a_key] = new_value; // set
do_something_with(my_map[a_key]); // get and use...
If you want to add new functions, they probably wouldn't look like what you're proposing because:
your set is only given one parameter despite needing a key and value (admittedly, you could adopt some convention like having the first ':' or '=' separate them), and
the get() function doesn't provide any key.
You could instead have something more like:
void set(const Key&, const Value&);
std::string get(const Key&) const;
But, even if you have write permissions to do so, you shouldn't add that directly in the map header file - all C++ programs compiled on that computer will share that file and won't expect it to be modified. Any small mistake could cause trouble, and if you ship your program to another computer you won't be able to compile it there without making a similar modification - if that computer uses a different C++ compiler the necessary details of that modification may be slightly different too.
So, you can either write your own (preferably templated) class that derives from (inherits) or contains (composition) a std::map, providing your functions in your custom class. An inheritance based solution is easier and more concise to write:
template <typename Key, typename Value>
struct My_Map : std::map<Key, Value>
{
My_Map(...); // have to provide any non-default constructors you want...
void set(const Key& key, const Value& value) { operator[](key) = value; }
// if you want entries for non-existent keys to be created with a default Value...
Value& get(const Key& key) { return operator[](key); }
--- OR ---
// if you want an exception thrown for non-existent keys...
Value& get(const Key& key) { return at(key); }
const Value& get(const Key& key) const { return at(key); }
};
This is slightly dangerous if you're planning to pass My_Maps around by pointer and accidentally end up with a "new My_Map" pointer that's later deleted as a std::map pointer, as in:
void f(std::map<int, string>* p) { /* use *p */ delete p; }
My_Map<int, string>* p = new My_Map<int, string>;
f(p);
Still, in most programs there's no real danger of accidentally disposing of a map like this, so go ahead and do it.
Further, and this is the kind of thinking that'll make me unpopular with the Standard-fearing purists around here - because My_Map hasn't added any data members or other bases, the std::map<> destructor probably does all the necessary tear-down even though it's technically Undefined Behaviour. I'm NOT encouraging you to ignore the issue (and would consider it unprofessional in a job requiring robustness), but you can at least rest a little easier. I'd be curious to hear from anyone with any compiler/settings where it demonstrably doesn't operate safely.
If you use composition, you'll have to write your own "forwarding" functions to let you use My_Map like a std::map, accessing iterators, find, erase, insert etc.. It's a pain.
Setter and getter for std::map is no different except that you need to pass the necessary parameters for the setter. Assume if I have a struct and has a member variable whose type is std::map, whose key is of type char and data is of type int. Method signatures would be of the format -
void setEncode( char* key, int* data, const int& size ); Because, std::map requires a key, data and sizes of these arrays being passed. With out knowing size, it is unknown as how far to insert the elements in to the container.
std::map<char, int> getEncode() const ; const key word signifies it a non-modifying member function. Because it's functionality is to just return a variable of type std::map.
Example -
struct myMap
{
std::map<char, int> encode;
void setEncode( char* key, int* data, const int& size );
std::map<char, int> getEncode() const ;
};
void myMap::setEncode( char *key, int* data, const int& size )
{
int i=0;
while( i < size )
{
encode.insert(std::pair<char, int>(key[i], data[i]));
++i ;
}
}
std::map<char, int> myMap::getEncode() const
{
return encode;
}
Results IdeOne. This should give you an idea, but should also follow the general rules what #templatetypedef, #tony suggested.
Do you want to set a key value pair in an existing map(probably that's what you want) or create a new map itself?
void setvar(string key, int value)
{
myMap[key] = value;
}
int getVar(string key) const
{
return myMap[key];
}
where int and string are interchangeable
For latter you'll probably have to interate over all map values for setting and getter should be just to return that map pointer.

Access to map data

I have a complex map that defined
typedef short short1
typedef short short2
typedef map<short1,short2> data_list;
typedef map<string,data_list> table_list;
I have a class that fill table_list
class GroupingClass
{
table_list m_table_list;
string Buildkey(OD e1){
string ostring;
ostring+=string(e1.m_Date,sizeof(Date));
ostring+=string(e1.m_CT,sizeof(CT));
ostring+=string(e1.m_PT,sizeof(PT));
return ostring;
}
void operator() (const map<short1,short2>::value_type& myPair) {
OD e1=myPair.second;
string key=Buildkey(e1);
m_table_list[key][e1.m_short1]=e1.m_short2;
}
operator table_list() {
return m_table_list;
}
};
and I use it by
table_list TL2
GroupingClass gc;
TL2=for_each(mapOD.begin(), mapOD.end(), gc);
but when I try to access to internal map I have problems
for example
data_list tmp;
tmp=TL2["AAAA"];
short i=tmp[1]; //I dont update i variable
and when debug it, I see code try add new value to map.then when tmp var is const this cause to a error
but if i use a loop by itrator this work properly
why this no work at first way
thanks herzl
std::map<Key,Value>::operator[](Key const& k) will look for key k. If it's not found, it will insert a new Value() and return a reference to that. This is a modifying operation. Therefore, operator[] is non-const.
The code does not make sense. What is OD? myPair.second would be short in operator() but then you do e1.m_short2 which does not make any sense.
Your likely bug is that for_each takes its 3rd parameter by value which means it is going to make copies of that map and probably you will lose the changes.
You can force for_each to take the 3rd value by reference either by specifying the template parameter directly or by wrapping in boost::ref. However you would do far better to make your functor take the map it is building by reference in its constructor rather than have an instance of a map.
class GroupingClass
{
table_list & m_table_list;
string Buildkey(OD e1)
{
string ostring;
ostring+=string(e1.m_Date,sizeof(Date));
ostring+=string(e1.m_CT,sizeof(CT));
ostring+=string(e1.m_PT,sizeof(PT));
return ostring;
}
public:
explicit GroupingClass( table_list & the_table_list ) :
m_table_list( the_table_list )
{
}
void operator() (const map<short1,short2>::value_type& myPair) const
{
OD e1(myPair.first); // probably what you meant
string key=Buildkey(e1);
m_table_list[key][e1.m_short1]=e1.m_short2;
// did you mean m_table_list[key][myPair.first] = myPair.second; ?
}
};
and use it by
table_list TL2;
for_each(mapOD.begin(), mapOD.end(), GroupingClass(TL2));

C++ map in a class, how do I make a member accessor

I'm new to maps. I need a class that constructs seating for a performance (there are many performances). Here's what I have so far:
// header stuff/libraries
Seats::Seats()
{
map< const string, bool > seat;
seat["A1"] = false;
seat["A2"] = false;
/* more seats .. */
}
Do I need to create an access member if I want to update a seat? If can so can I have an example please?
as others have indicated, your map variable needs to be a data member of the class not in the local scope of the Seats constructor as you've posted
class Seats {
public:
Seats();
bool GetSeat(const string &);
void SetSeat(const string &, bool);
private:
map< string, bool > seat;
};
Seats::Seats() {
// merely your example values posted.
seat["A1"] = false;
seat["A2"] = false;
}
void Seats::SetSeat(const string &seat_number, bool occupied) {
seat[seat_number] = occupied;
}
bool Seats::GetSeat(const string &seat_number) {
return seat[seat_number];
}
keep in mind using the map's [] operator though can cause elements to be inserted into the data structure if they do not exist yet:
link text
T& operator[] ( const key_type& x );
If x does not match the key of any
element in the container, the function
inserts a new element with that key
and returns a reference to its mapped
value. Notice that this always
increases the map size by one, even if
no mapped value is assigned to the
element (the element is constructed
using its default constructor).
This might get you started.
class Seats {
private: // private section: implementation details here
set< string > reservations;
public: // public section: accessors here
bool is_reserved( string const &id ) const {
return reservations.count( id );
}
bool reserve( string const &id ) { // return false if res. already existed
return reservations.insert( id ).second;
}
};
If you want to use a container (like std::map) as a data member in a class, you need to put it with the rest of the data members for the class, in the class definition.
A curios (intentional) property of std::map is that operator[] default constructs values for keys that aren't already in the collection, so unless you actually need to get a list of the (false by default) keys, there's no need to initialize them.