How is mymap[key]++ visualized. Am I incrementing the key or the value? In LINE 1 hash[numberToFind] is the key or the value that I am inserting into the result?
unordered_map<int, int> hash;
vector<int> result;
for (int i = 0; i < arr.size(); i++) {
int numberToFind = target - arr[i];
if (hash.find(numberToFind) != hash.end()) {
result.push_back(hash[numberToFind]); //LINE 1
result.push_back(i);
return result;
}
Consider going to a good reference before asking on stackoverflow.com. cppreference.com is pretty good.
https://en.cppreference.com/w/cpp/container/unordered_map
key is the key.
mymap[key] returns a reference to the element in mymap at key key.
If key is not yet in mymap, mymap[key] default constructs an element at key and then returns a reference to it.
mymap[key]++ increments the element in mymap at key key. The rule from above still holds: if key is not yet in mymap, then an element is default constructed in mymap at key key, and then incremented.
myvec.push_back(mymap[key]) creates a copy of the value at key, and adds it to the end of myvec. The copying a a function of how push_back works. push_back only adds copies of elements to the end of the vector. The rule from above still holds: if key is not yet in mymap, then an element is default constructed in mymap at key key, and then copied into myvec.
numbers[i] is a reference to the ith element in std::vector<int> numbers. Vectors do not default create elements at an index if there's not already an element at that index. Instead, accessing an out of bounds index is undefined behavior.
hash[x] is a reference to the element in std::unordered_map<int, int> hash at key x. Default creates, as above, if necessary.
hash[numbers[i]] is a reference to the element in hash at key numbers[i]. Default creates as above, if necessary.
hash[numbers[i]] = i; sets the element in hash at key numbers[i] to the value of i.
Related
I am learning new C++17 features and I came across this:
std::vector<int> nums = { 1, 1, 2, 3 };
std::unordered_map<int, size_t> m;
for (int i = 0; i < nums.size(); ++i)
{
const auto& [inserted_entry, inserted_happen] = m.emplace(nums[i], i);
std::cout << inserted_happen << "\n";
}
The result is:
1
0
1
1
What is happening here? I do not understand.
Also what is inserted_entry?
emplace returns a pair of an iterator to inserted element(or already present element) and a bool representing if the insert was successful.
inserted_happen is a bool.
The second insert fails since 1 already exists as key in the map.
emplace returns std::pair<iterator, bool> which then gets "destructured" and 2 bindings get created. inserted_entry is a reference to the iterator part and inserted_happen is a reference to the bool part.
Return value of emplace is a pair.
The first element of the pair is the iterator for the newly inserted element (if insertion was successful) or of an existing element (if insertion failed).
The second part of the returned pair is a bool value (in your case inserted_happen) indicating whether insertion succeeded.
In your case, the second emplace failed as you have already inserted an element with key equal to 1.
If you want to change the value part of the element, you can just try -
m[nums[i]] = i;
void main()
{
std::map<int,int>keyValueMap;
keyValueMap.insert(0,1);
keyValueMap.insert(0,2);
int index = keyValueMap.begin()->second;
}
the value of index at thispoint is 1 and not 2 can someone explain why ?
From this std::map::insert reference:
Inserts element(s) into the container, if the container doesn't already contain an element with an equivalent key.
[Emphasis by me]
You can't use insert to overwrite existing data in the map.
All of the 2-param overloads of std::map::insert() require iterators, not key/value pairs.
Most standard container insert() methods take the container's value_type as input. The value_type of a std::map is a std::pair. So, when inserting a key/value pair into a std::map, you have to pass them in to insert() as a std::pair, eg:
void main()
{
std::map<int,int> keyValueMap;
keyValueMap.insert(std::make_pair(0, 1));
keyValueMap.insert(std::make_pair(0, 2));
int index = keyValueMap.begin()->second;
}
Now, that being said, the second insert() will still not overwrite the value from the first insert(), even though they both have the same key 0. The second insert will simply be ignored:
Inserts element(s) into the container, if the container doesn't already contain an element with an equivalent key.
...
Return value
1-3) Returns a pair consisting of an iterator to the inserted element (or to the element that prevented the insertion) and a bool denoting whether the insertion took place.
To overwrite the first value with the second value, you can either:
use the iterator that insert() returns:
void main()
{
std::map<int,int> keyValueMap;
keyValueMap.insert(std::make_pair(0, 1));
auto ret = keyValueMap.insert(std::make_pair(0, 2));
if (!ret.second) ret.first->second = 2;
int index = keyValueMap.begin()->second;
}
use the map's operator[] instead:
void main()
{
std::map<int,int> keyValueMap;
keyValueMap[0] = 1;
keyValueMap[0] = 2;
int index = keyValueMap.begin()->second;
}
I have an unordered map that stores a string as its key and an iterator to a spot in a vector as its data. Each element in the vector holds a string and an int (number of times the string appears). I have coded an increaseCount(std::string, int) function that is supposed to insert the new string into the unordered map, unless it is already within the container. If this is the case, the function should find the key in the unordered map, got to the corresponding location in the vector that the iterator points to, and add one to the int parameter of the vector element. However, when executing the second case, the error "Vector iterator not dereferencable" shows up. Here is what I have coded.
void ourTrends::increaseCount(std::string s, unsigned int amount){
// check to see if key is already in
if(wordStoreTable.find(s) == wordStoreTable.end()){
// add the element into the hash table
std::vector<std::pair<std::string, int>>::iterator it;
std::pair<std::string, std::vector<std::pair<std::string, int>>::iterator> word (s, it);
wordStoreTable.insert(word);
// add element to back of vector
std::pair<std::string, int> p1 (s, amount);
sortedVector.push_back(p1);
//std::swap(sortedVector.front(), sortedVector.back());
// set the iterator of the hash pair to the end of the current vector size
it = sortedVector.end();
--it;
wordStoreTable.find(s)->second = it;
isSorted = false;
} else{
int x = wordStoreTable.find(s)->second->second;
std::pair<std::string, int> p1 (s, x + amount);
sortedVector.erase(wordStoreTable.find(s)->second);
sortedVector.push_back(p1);
//std::swap(sortedVector.begin(), sortedVector.end());
std::vector<std::pair<std::string, int>>::iterator it = sortedVector.end();
--it;
wordStoreTable.find(s)->second = it;
std::cout << wordStoreTable.find(s)->first << std::endl;
}
}
I know that this means the iterator is pointing to an empty location in memory, but I cannot figure out where it loses track of its destination.
The reason this code doesn't work, is that vector::push_back invalidates the iterators, that is, an iterator you had for a vector of size 3, might not work if you make the vector larger by adding a new element. From cppreference: If the new size() is greater than capacity() then all iterators and references (including the past-the-end iterator) are invalidated. Otherwise only the past-the-end iterator is invalidated.
You certainly could reserve enough space for the vector ahead of time, so that iterators do not invalidate, but as a general rule, you're better off with using numerical indices.
map<string, string> dada;
dada["dummy"] = "papy";
cout << dada["pootoo"];
I'm puzzled because I don't know if it's considered undefined behaviour or not, how to know when I request a key which does not exist, do I just use find instead ?
The map::operator[] searches the data structure for a value corresponding to the given key, and returns a reference to it.
If it can't find one it transparently creates a default constructed element for it. (If you do not want this behaviour you can use the map::at function instead.)
You can get a full list of methods of std::map here:
http://en.cppreference.com/w/cpp/container/map
Here is the documentation of map::operator[] from the current C++ standard...
23.4.4.3 Map Element Access
T& operator[](const key_type& x);
Effects: If there is no key equivalent to x in the map, inserts value_type(x, T()) into the map.
Requires: key_type shall be CopyConstructible and mapped_type shall be DefaultConstructible.
Returns: A reference to the mapped_type corresponding to x in *this.
Complexity: logarithmic.
T& operator[](key_type&& x);
Effects: If there is no key equivalent to x in the map, inserts value_type(std::move(x), T()) into the map.
Requires: mapped_type shall be DefaultConstructible.
Returns: A reference to the mapped_type corresponding to x in *this.
Complexity: logarithmic.
If you try to access a key value using index operator [], then 2 things can happen :
The map contains this key. So it will return the corresponding key value.
The map doesn't contain the key. In this case, it will automatically add a key to the map with null value.
"pootoo" key does't exist in your map. So it will automatically add this key with value = ""(empty string). And your program will print empty string.
Here map size will increase by 1.
To search a key you can use map_name.find(), which will return map_name.end() if the key doesn't exist. And no extra key will be added.
You can use [] operator when you want to set value for a key.
It's not undefined behavior. If operator [] doesn't find a value for the provided key, it inserts one at that position.
For operator[], if you try to access a value for a key that doesn't exist, a new value object that has been default constructed will be put into the map and it's reference returned.
The operator[] for map returns a non-const reference and you can assign using that in the way you've shown on your second line. Accessing in this way will create a default contructed element of value type.
If you want to find a find an element, a better way is
iterator find ( const key_type& x )
(or the const alternative) which will return an iterator equal to <map>.end() if it doesn't find the key, or if you just want to know if it's in the collection you can use
size_type count ( const key_type& x ) const
which will always return either 1 or 0 for a map since keys are unique.
If operator [] doesn't find a value for the provided key, it inserts one at that position.
But you should note that if you visit a not exist key and invoke it's member function, like mapKV[not_exist_key].member_fun().The program may crash.
Let me give an example, test class as below:
struct MapValue{
int val;
MapValue(int i=0){
cout<<"ctor: "<<i<<endl; val = i;
}
~MapValue(){
cout<<"dtor: "<<val<<endl;
}
friend ostream& operator<<(std::ostream& out, const MapValue& mv){
cout<<"MapValue: "<<mv.val<<endl;
}
string toString(){
cout<<"MapValue: "<<val<<endl;
}
};
Test code:
cout<<"-------create map<int, MapValue>-------"<<endl;
map<int, MapValue> idName{{1, MapValue(1)}, {2, MapValue(2)}};
cout<<"-----cout key[2]-----"<<endl;
cout<<idName[2]<<endl;
cout<<"-----cout key[5]-----"<<endl;
cout<<idName[5]<<endl;
cout<<"------- runs here means, does't crash-------"<<endl;
Output as below:
-------create map<int, MapValue>-------
ctor: 1
ctor: 2
dtor: 2
dtor: 1
dtor: 2
dtor: 1
-----cout key[2]-----
MapValue: 2
-----cout key[5]-----
ctor: 0
MapValue: 0
-------runs here means, does't crash-------
dtor: 0
dtor: 2
dtor: 1
We can see that: idName[5] invoke map construct {5, MapValue(0)} to insert to idName.
But if, you invoke member function by idName[5], then the program crashes :
cout<<"-------create map<int, MapValue>-------"<<endl;
map<int, MapValue> idName{{1, MapValue(1)}, {2, MapValue(2)}};
idName[5].toString(); // get crash here.
cout<<"------- runs here means, doesn't crash-------"<<endl;
please have a look at the out_of_range exception:
http://www.cplusplus.com/reference/stdexcept/out_of_range/
this is what map::at and map::operator[] will throw if key does not exist. You can catch it the same way as the vector example in the link.
You can also use:
http://www.cplusplus.com/reference/map/map/find/
And check against map::end
std::map<std::string, int> m;
// Can I make assumption that m["NoSuchKey"] will return 0?
std::cout << m["NoSuchKey"] << std::endl;
Yes. When an item is accessed through operator[] that does not exist, it is created with a default-constructed value, and returned.
For numeric types, default-constructed means 0.
Yes. If the map key is not found, a default value will be inserted.
Specifically, operator[] is understood as:
(*((m.insert(value_type(k, data_type()))).first)).second
Translated to your case, m["NoSuchKey"] means
std::pair<std::string, int> pair_to_insert ("NoSuchKey", 0);
// default value of int is 0.
std::pair<std::map<std::string, int>::iterator, bool>
insert_res = m.insert(value_to_insert);
std::map<std::string, int>::iterator iter_of_inserted_pair = insert_res.first;
std::pair<std::string, int> inserted_pair = *iter_of_inserted_pair;
int inserted_value = inserted_pair.second;
return inserted_value;
In particular, m.insert returns the old key-value pair if the key already exists, and the new key-value pair if not. Therefore, you'll get the expected value if the key already exists, and 0 (the default value) if not.