Is unordered map behaving as expected in C++? - c++

I am new to C++ and I don't get to understand the logic of the following code:
#include <unordered_map>
std::unordered_map<int, int> maxima;
void update(int key, int value) {
auto it = maxima.find(key);
if (it == maxima.end()) {
maxima.emplace(key, value);
} else if (value > it->second) {
it->second = value;
}
}
I have tested it with the following:
int main() {
update(3,6);
update(1,6);
update(4,6);
update(4,9);
update(2,9);
update(1,3);
update(3,18);
for (auto x : maxima)
cout << x.first << " " << x.second << endl;
return 0;
}
and it returns:
2 9
4 9
1 6
3 18
How come the (1,3) has disappeared from the output?

The update function can be read as if the key is not present then add the key/value pair to the map, otherwise if the new value is greater than the existing value then replace the existing value with the new value, otherwise do nothing.
I'm sure you can see that under this rule the (1,3) pair never gets added because of the existing (1,6) pair.
The overall logic is to store in the unordered map the maximum value associated with any given key. For the key of 1 that value is 6.

Related

Counting Duplicates in C++ - multiset?

UPD:-
Value Instances
 2   3
 3   2
 5   1
I want to limit the count to 1 for all the instances present in the multiset.
#include<bits/stdc++.h>
using namespace std;
int main() {
multiset<int> p1;
p1.insert(5);
p1.insert(2);
p1.insert(3);
p1.insert(3);
p1.insert(2);
p1.insert(2);
for(auto itr : p1) {
if(p1.count(itr) > 1)
p1.erase(itr);
cout << itr;
}
}
How to fix this ?
My comment:
In that case, you should use a std::set<int> because that is actually what matches your requirement. You could use also a std::map<int, int> to map the key to the number of occurrences if you like.
OPs reply:
Can you add this to a full-fledged answer so that I can accept it for this question?
Here we go:
Just filtering duplicates:
#include <iostream>
#include <set>
int main()
{
int sample[] = { 5, 2, 3, 3, 2, 2 };
// add all values at most once
using Table = std::set<int>;
Table table;
for (int value : sample) table.insert(value);
// output the result
for (const Table::value_type& entry : table) {
std::cout << "Value " << entry << "\n";
}
}
Output:
Value 2
Value 3
Value 5
Demo on coliru
Counting the number of occurrences:
#include <iostream>
#include <map>
int main()
{
int sample[] = { 5, 2, 3, 3, 2, 2 };
// add all values at most once but count the number of occurrences
using Table = std::map<int, unsigned>;
Table table;
for (int value : sample) ++table[value];
// output the result
for (const Table::value_type& entry : table) {
std::cout << "Value " << entry.first << " (" << entry.second << " times)\n";
}
}
Output:
Value 2 (3 times)
Value 3 (2 times)
Value 5 (1 times)
Demo on coliru
The trick:
The std::map::operator[] inserts an element if the key is not yet there. This element (in this case std::pair<const int, unsigned>) is default initialized which grants that it starts as { key, 0 }.
So, there are two cases:
The key is not yet there:
The element is created as { key, 0 } and the value (.second of the element) is incremented immediately which results in { key, 1 }.
The key is already there:
The value (.second of the element) is incremented again.
A variation on filtering duplicates:
This keeps the original input order but removes repetitions (by book-keeping in a separate std::set).
#include <iostream>
#include <set>
#include <vector>
int main()
{
using Sample = std::vector<int>;
Sample sample = { 5, 2, 3, 3, 2, 2 };
// remove duplicates
using Table = std::set<int>;
Table table;
Sample::iterator iterRead = sample.begin();
Sample::iterator iterWrite = sample.begin();
for (; iterRead != sample.end(); ++iterRead) {
if (table.insert(*iterRead).second) *iterWrite++ = *iterRead;
}
sample.erase(iterWrite, sample.end());
// output the result
for (const Sample::value_type& entry : sample) {
std::cout << "Value " << entry << "\n";
}
}
Output:
Value 5
Value 2
Value 3
Demo on coliru
The trick:
std::set::insert() returns a pair of iterator and bool.
The iterator points to the key in the set (inserted or already been there).
The bool denotes if the key was inserted (true) or was already there (false).
The other trick:
Just erasing every found duplicate from the std::vector would result in the worse complexity O(n²).
Hence, two iterators are used, one for reading and one for writing. Thereby, every input value which is not yet in the bookkeeping table (and hence occurs the first time) is written back, otherwise not.
So, every value which occurred the first time is shifted towards the beginning and appended to the previous values which occurred the first time each. Additionally, the iterWrite points past the last written element after the loop and can be used to erase the rest (which contains left input values which are all duplicates).
The complexity of this algorithm is O(n) – much better than the naive approach.
Btw. the standard algorithms std::remove(), std::remove_if() does it the same way.
Thus, the same algorithm could be achieved with std::remove_if():
#include <algorithm>
#include <iostream>
#include <set>
#include <vector>
int main()
{
using Sample = std::vector<int>;
Sample sample = { 5, 2, 3, 3, 2, 2 };
// remove duplicates
using Table = std::set<int>;
Table table;
Sample::iterator last
= std::remove_if(sample.begin(), sample.end(),
[&](int value) { return !table.insert(value).second; });
sample.erase(last, sample.end());
// output the result
for (const Sample::value_type& entry : sample) {
std::cout << "Value " << entry << "\n";
}
}
Output:
like above
Demo on coliru
#include <iostream>
#include <set>
using namespace std;
int main()
{
multiset<int> p1;
p1.insert(5);
p1.insert(2);
p1.insert(3);
p1.insert(4);
p1.insert(2);
p1.insert(2);
for (auto iter = p1.begin(); iter != p1.end();)
{
p1.count(*iter) > 1 ? iter = p1.erase(iter) : iter++;
}
for (auto & iter : p1)
{
cout << iter << ", ";
}
return 0;
}

Find value of index in Map Array using string key

In an array map<string, int> bannd such that each key (of type string) holds a number value, like this
+++++++++++++++
key | value
+++++++++++++++
red | 0
blue | 1
orange| 3
etc...
What is the optimal way to return the value of an index using the key?
I already tried using find like this
band1 = band.find("a");
where a is the key value in the map, but it does not seem to be working.
find returns an iterator pointing to the found key-value pair (if any). You have to dereference that iterator to get the actual mapped value:
int band1;
auto it = band.find("a");
if (it != band.end())
band1 = it->second;
else
/* not found ... */;
Note that *it just gives us the std::pair containing the key and mapped value together. To access the mapped value itself we use it->second.
Alternatively, if you know that the key is in the map, you can use at to get the mapped value for that key:
int band1 = band.at("a");
at will throw an out_of_range exception if the element is not found.
Finally, if you want to access the value with key "a" and you want to automatically add that key to the map if it is not already there, you can use the subscript operator []:
int band1 = band["a"]; //warning: inserts {a, 0} into the map if not found!
Write a function, which takes std::map and std::vector of key as argument. And it will return the corresponding values in std::vector
vector<int> valueReturn(map<string,int> data, vector<string> key) {
vector<int> value;
for(const auto& it: key) {
auto search = data.find(it);
if(search != data.end()) {
value.push_back(data[it]);
std::cout << "Found " << search->first << " " << search->second << '\n';
}
else {
value.push_back(-1); // Inserting -1 for not found value, You can insert some other values too. Which is not used as value
std::cout << "Not found\n";
}
}
return value;
}
int band1 = band["a"];
int band2 = band["b"];
int band3 = band["c"];
int band4 = band["d"];

FIFO Map in c++

I need to store a number of key/value pairs and access them again referenced by key - not necessarily in a map, although this seems natural. Additionally, if the map exceeds a certain size, I need to delete the oldest pairs.
Is there a way to implement this using a map or a similar structure somehow combining a map and a queue in C++11?
UPDATE: I wanted to this with a std::unsorted_map. Unfortunately I'm heavily missing std::map functions which would help. The unordered list seems neither to support rbegin() nor does its iterator support the --operator, so that I can't use end() either.
Is there a better way than iterating through a loop to size()-1?
There's no unique solution for this problem, the simplest one would be to use an auxiliary queue for storing the keys in order of insertion.
map<string, string> m_myMap;
queue<string> m_myQueue;
void insert(const string& key, const string& value) {
m_myMap.insert(make_pair(key, value));
m_myQueue.push(key);
}
void deleteOldOnes() {
while (m_myQueue.size() > MAX_SIZE) {
m_myMap.erase(m_myQueue.front());
m_myQueue.pop();
}
}
You keep using the map for accessing the elements by key, the queue should not be used anywhere else than in the two methods above.
I had the same problem every once in a while and here is my solution: https://github.com/nlohmann/fifo_map. It's a header-only C++11 solution and can be used as drop-in replacement for a std::map.
Example
#include "src/fifo_map.hpp"
// for convenience
using nlohmann::fifo_map;
int main() {
// create fifo_map with template arguments
fifo_map<int, std::string> m;
// add elements
m[2] = "two";
m[3] = "three";
m[1] = "one";
// output the map; will print
// 2: two
// 3: three
// 1: one
for (auto x : m) {
std::cout << x.first << ": " << x.second << "\n";
}
// delete an element
m.erase(2);
// re-add element
m[2] = "zwei";
// output the map; will print
// 3: three
// 1: one
// 2: zwei
for (auto x : m) {
std::cout << x.first << ": " << x.second << "\n";
}
}
Note how the fifo_map's elements are always printed in the order of the insertion. Deletion of old elements is not implemented, but this extension should not be too difficult.
#include<iostream>
#include<queue>
using namespace std;
main(){
queue < pair <int,int> > Q; //First use a queue to store the pair wise values
int a,b;
// insert value to the queue as a pair
for (int i=0;i<6;i++){ // i only insert 6 pairs
cin>>a>>b;
if (Q.size()>=3){ // if queue size is 3 than pop up the first value
Q.pop();
}
Q.push(make_pair(a,b)); // insert a new pair into the queue
}
while(!Q.empty()){ // output the pairs on that queue
cout<<Q.front().first<<" "<<Q.front().second<<endl;
Q.pop();
}
return 0;
}

C++: Map, previous item of a key

I have this map: map<int, int > items.
Given a key, I want that this map returns the item corrisponding to the key if it present, otherwise the map returns the item with key immediately less than the given key.
For example, if I have:
items[0]=0;
items[6]=10;
items[15]=18;
items[20]=22;
than for key=15, I want that the map returns item with value 18, otherwise for key=9, I want that map returns item with value 10.
I haven't find a function for this case. But I tried in this way:
itlow=items.lower_bound(key);
if(!items.count(key))
itlow--;
return itlow->second;
This works as I want, entering in the map a min value items[0]=0 for default, but I know that itlow--; it's not good programming. How can I do? thanks all.
You just need to check if your itlow is already items.begin(). If it is, there's no such element in the map:
itlow=items.lower_bound(key);
if(itlow->first == key)
return itlow->second;
else if(itlow != items.begin())
itlow--;
return itlow->second;
else
throw some_exception();
Instead of throwing exception, you may return iterator, and then you can return items.end() if no such element is found.
#include <iostream>
#include <map>
using namespace std;
map<int, int>::const_iterator find(const map<int, int> &items, int value)
{
auto itlow = items.lower_bound(value);
if(itlow->first == value)
return itlow;
else if(itlow != items.cbegin())
return --itlow;
else
return items.cend();
}
int main()
{
map<int, int> items;
items[2]=0;
items[6]=10;
items[15]=18;
items[20]=22;
auto i = find(items, 0);
if(i != items.cend())
{
cout << i->second << endl;
}
i = find(items, 15);
if(i != items.cend())
{
cout << i->second << endl;
}
i = find(items, 9);
if(i != items.cend())
{
cout << i->second << endl;
}
}
Try this
auto it = prev(map.upper_bound(key));
This works because map.upper_bound returns an iterator pointing to the first element that is greater than key or the past-the-end iterator when such element does not exist. Also, OP explained that map is not empty and key is greater than the first element in the map. If the latter conditions are not met, one should handle separately the case where upper_bound returns map.begin().

How to use vector and struct?

I need to count letters from the string, sort them by count and cout results. For this purpose I'm trying to use vector and struct. Here is part of my code, but it's not working, because I don't know how to implement something:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
struct int_pair{
int key;
int value;
};
bool sort_by_value(int_pair left, int_pair right){
return left.value < right.value;
}
int main() {
string characters = "aasa asdfs dfh f ukjyhkh k wse f sdf sdfsdf";
vector<int_pair> most_frequent;
for (string::size_type i = 0; i <= characters.length(); i++) {
int int_char = (int)characters[i];
most_frequent[int_char]++; <-- I want to do something like this, but it's not working
}
sort(most_frequent.begin(), most_frequent.end(), sort_by_value);
for (vector<int_pair>::iterator it = most_frequent.begin(); it != most_frequent.end(); ++it) <-- is this call correct?
cout << " " << it->key << ":" << it->value << endl;
return 0;
}
At this code I have 2 parts that I don't know how to deal:
most_frequent[int_char]++; <-- I want to do something like this, but it's not working
and
for (vector<int_pair>::iterator it = most_frequent.begin(); it != most_frequent.end(); ++it) <-- is this call correct?
Maybe you can see any other mistakes and potential issues at this code.
I would use a std::map to determine the frequency of each letter, then copy that into a multimap while reversing the key and value to get them in order.
#include <iostream>
#include <map>
#include <algorithm>
template<class T, class U>
std::pair<U,T> flip_pair(const std::pair<T,U>& p) {
return std::make_pair(p.second,p.first);
}
int main(){
std::string characters = "zxcvopqiuweriuzxchajksdui";
std::map<char,int> freq;
std::multimap<int,char> rev_freq;
// Calculate the frequency of each letter.
for(char c: characters){
freq[c]++;
}
// Copy the results into a multimap with the key and value flipped
std::transform(std::begin(freq), std::end(freq),
std::inserter(rev_freq, rev_freq.begin()),
flip_pair<char,int>);
// Print out the results in order.
for(std::pair<int,char> p : rev_freq){
std::cout << p.first << ": " << p.second << std::endl;
}
};
This should do what you need:
most_frequent[int_char].key = int_char;
most_frequent[int_char].value++;
Yes, it sets the key many times, even though it doesn't need to.
When accessing the container with the key (vector is indexed with an integer, which is "the key" in your case), you don't have to store the key in the value field of the container again.
So you don't need your struct since you only need the value field and can can store the number of occurrences directly in the vector.
The idea is to fill the vector with 256 integers in the beginning, all initialized to zero. Then, use the vector index as your "key" (character code) to access the elements (number of occurrences).
This will result in a code similar to this:
// initialize with 256 entries, one for each character:
vector<int> counts(256);
for (string::size_type i = 0; i <= characters.length(); i++) {
// for each occurrence of a character, increase the value in the vector:
int int_char = (int)characters[i];
counts[int_char]++;
}
Once filling of the vector is done, you can find the maximum value (not only the value but also the key where it is stored) using the std::max_element algorithm:
vector<int>::iterator most_frequent =
std::max_element(counts.begin(), counts.end());
// getting the character (index within the container, "key"):
std::cout << (char)(most_frequent - counts.begin());
// the number of occurrences ("value"):
std::cout << (*most_frequent);
Here is your example with the changes (only printing the most frequent character, here it is the space so you don't see it): http://ideone.com/94GfZz
You can sort this vector, however, you will loose the key of course, since the elements will move and change their indices. There is a nice trick to process statistics like that: Use a reversed (multi)map (key, value reversed):
multimap<int,int> keyForOccurrence;
for (vector<int>::iterator i = counts.begin(); i != counts.end(); ++i) {
int occurrences = *i;
int character = i - counts.begin();
keyForOccurrence.insert(std::pair<int,int>(occurrences, character));
}
Updated code: http://ideone.com/Ub5rnL
The last thing you should now sort out by yourself is how to access and process the data within this map. The fancy thing about this reversed map is that it is now automatically sorted by occurrence, since maps are sorted by key.
I find more natural to use a std::map container to store each character occurrences. The character is map's key, its occurrence count is map's value.
It's easy to scan the source string and build this map using std::map::operator[], and ++ to increase the occurrence count.
Then, you can build a second map from the above map, with key and value inverted: so this map will be sorted by occurrences, and then you can print this second map.
Note that you have to use a std::multimap as this second map, since its keys (i.e. the occurrences) can be repeated.
Sample code follows (I tested it with VS2010 SP1/VC10):
#include <stddef.h> // for size_t
#include <algorithm> // for std::transform
#include <functional> // for std::greater
#include <iostream> // for std::cout
#include <iterator> // for std::inserter
#include <map> // for std::map, std::multimap
#include <ostream> // for std::endl
#include <string> // for std::string
#include <utility> // for std::pair
using namespace std;
int main()
{
string str = "aasa asdfs dfh f ukjyhkh k wse f sdf sdfsdf";
// Build the occurrences map (char -> occurrences)
map<char, size_t> freq;
for (size_t i = 0; i < str.length(); ++i)
freq[ str[i] ]++;
// Build a new map from previous map with inverted <key, value> pairs,
// so this new map will be sorted by old map's value (i.e. char's
// occurrences), which is new map's key.
// Use the std::greater comparator to sort in descending order.
multimap<size_t, char, greater<size_t>> sorted_freq;
transform(
freq.begin(), freq.end(), // source
inserter(sorted_freq, sorted_freq.begin()), // destination
[](const pair<char, size_t>& p) // invert key<->value
{
return pair<size_t, char>(p.second, p.first);
}
);
// Print results
for (auto it = sorted_freq.begin(); it != sorted_freq.end(); ++it)
cout << it->second << ": " << it->first << endl;
}
Output:
: 9
s: 7
f: 7
d: 5
a: 4
k: 3
h: 3
u: 1
w: 1
y: 1
j: 1
e: 1
If you don't want to print the space character occurrences, you can easily filter that out.
Note that using std::map/std::multimap will also scale up better than std::vector for non-ASCII characters, e.g. if you use Unicode UTF-32 (since Unicode characters are much more than just 256).