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;
}
Related
I have the following problem - I want to count the occurrences of each word in a file. I'm using a map<string,Count> so the key is the string object representing the word, and the value being looked up is the object that keeps count of the strings so that :
class Count {
int i;
public:
Count() : i(0) {}
void operator++(int) { i++; } // Post-increment
int& val() { return i; }
};
The problem is that I want to use insert() instead of the operator[]. Here is the code.
typedef map<string, Count> WordMap;
typedef WordMap::iterator WMIter;
int main( ) {
ifstream in("D://C++ projects//ReadF.txt");
WordMap wordmap;
string word;
WMIter it;
while (in >> word){
// wordmap[word]++; // not that way
if((it= wordmap.find(word)) != wordmap.end()){ //if the word already exists
wordmap.insert(make_pair(word, (*it).second++); // how do I increment the value ?
}else{
...
}
for (WMIter w = wordmap.begin();
w != wordmap.end(); w++)
cout << (*w).first << ": "
<< (*w).second.val() << endl;
}
Could you refactor so as not to use find but simply attempt the insert?
Insert always returns a pair<iter*, bool>. The bool is 0 if it finds the key, and the iter* points to the existing pair. So we can take the pointer to the pair and increment the value:
// On successful insertion, we get a count of 1 for that word:
auto result_pair = wordmap.insert( { word, 1 } );
// Increment the count if the word is already there:
if (!result_pair.second)
result_pair.first->second++;
It was my first time posting. I'm learning C++ and welcome feedback on my idea.
The problem is that I want to use insert() instead of the operator[]
...why? std::map::insert cannot mutate existing values. operator[] is the right job for this.
If you really want to use insert (please don't), you first need to erase the existing value, if present:
if((it= wordmap.find(word)) != wordmap.end())
{
const auto curr = it->second; // current number of occurrences
wordmap.erase(word);
wordmap.insert(make_pair(word, curr + 1));
}
I am trying to write a program in C++ using maps...
My goal is to avoid the same values repeated in maps.
If the keys are same, we can use maps to avoid the duplicated keys. To allow duplicate keys, we use multimaps.
In case the value is the same, how can we avoid that?
The program which I have written allows duplicated values:
typedef std::map<int, std::string> MyMap;
int main()
{
MyMap map;
MyMap::iterator mpIter;
int key;
string value;
int count;
for(count = 0; count < 3;count++)
{
cin >> key;
cin >> value;
std::pair<MyMap::iterator, bool> res = map.insert(std::make_pair(key,value));
}
for (mpIter=map.begin(); mpIter != map.end(); ++mpIter)
cout << " " << (*mpIter).second << endl;
}
Make the value part of the key and/or use a set but that may not really solve the problem. It isn't possible to easily define a container that has both unique keys AND values if that's what you want. However, you might still construct one. Here's a very simple example to illustrate what is needed:
// Assuming keys are KEY and values are VAL
class MyMap {
public:
std::set<KEY> keyset;
std::set<VAL> valset;
std::map<KEY,VAL> theRealMap;
// assuming existence of function HAS(S,V)
// which returns true if v is in set S
bool MyInsert(KEY ky, VAL val) {
if (HAS(keyset, ky) return false;
if (HAS(valset, val) return false;
keyset.insert(ky);
valset.insert(vl);
return theRealMap.insert(std::pair<KEY,VAL>(ky, val));
}
:
:
Since this is an example it's not intended to be copied. You will likely want to include the functionality provided by std:map. An easy way would be to use std::map as a base class but you will need to hide (by making private) or implement similar code for each variant of insert otherwise you might get inadvertent insert that may not be unique.
Note: this requires twice the size of a single map. You can save some space by using theRealMap instead of a separate set for keys set. Another way would be to search the map but that sacrifices time for space. It's your call.
One way to do this is to maintain a separate std::set of the values. When you insert a value into a set it returns a std::pair<iterator, bool>. The bool value is true if the value was not already in the set. This tells you if it is safe to also put the value in the map.
First, however, you need to make sure the key is unique because the same key may already have been inserted with a different value:
typedef std::map<int, std::string> MyMap;
int main()
{
MyMap map;
MyMap::iterator mpIter;
int key;
string value;
int count;
// keep track of values with a std::set
std::set<std::string> values;
for(count = 0; count < 3; count++)
{
cin >> key;
cin >> value;
auto found = map.find(key);
if(found != map.end()) // key already in map
continue; // don't add it again
// now try to add it to the set
// only add to the map if its value is not already in the set
if(values.insert(value).second)
map.insert(std::make_pair(key, value));
}
for(mpIter = map.begin(); mpIter != map.end(); ++mpIter)
cout << " " << (*mpIter).second << endl;
}
One (inefficient) way to do it is to create a reverse map (with <string,int>) and insert your input in reverse order as that of MyMap into it. If ok, then insert into MyMap
Here is the working code.
typedef std::map<int, std::string> MyMap;
typedef std::map<string, int> rev_Map;
int main()
{
MyMap map;
rev_Map rmap;
MyMap::iterator mpIter;
rev_Map::iterator rmap_iter;
int key;
string value;
int count;
for(count = 0; count < 3;count++)
{
cin >> key;
cin >> value;
std::pair<rev_Map::iterator, bool> ok = rmap.insert(std::make_pair(value,key)); //insert into the reverse map
if(ok.second) //if above amap.insert works
std::pair<MyMap::iterator, bool> res = map.insert(std::make_pair(key,value));
}
for (mpIter=map.begin(); mpIter != map.end(); ++mpIter)
cout << " " << (*mpIter).second << endl;
}
I'am having problems while trying to iterate some maps.
Basically i have a Deposit class. Each deposit class has a multimap containing a destination Deposit and a distance. (This will be used to create a graph).
When i try to iterate all the maps i'm getting a segmentation fault error.
Here's the code:
for (int j = 0; j < deposit.size(); j++) {
for (typename multimap< Deposit<Product>*, int>::iterator it = deposit.at(j)->getConnections().begin(); it != deposit.at(j)->getConnections().end(); it++) {
cout << "From the depo. " << deposit.at(j)->getKey() << " to " << it->first->getKey() << " with the distance " << it->second << endl;
}
}
EDIT:
Deposit Class:
template<class Product>
class Deposit {
private:
multimap <Deposit<Product>*, int> connections;
public:
void addConnection(Deposit<Product>* dep, int dist);
multimap <Deposit<Product>*, int> getConnections() const;
};
(...)
template<class Product>
void Deposit<Product> ::addConnection(Deposit<Product>* depKey, int dist) {
this->connections.insert(pair<Deposit<Product>*, int>(depKey, dist));
}
template<class Product>
multimap < Deposit<Product>*, int> Deposit<Product> ::getConnections() const {
return this->connections;
}
Storage Class - This is where I populate the multimaps.
(...)
ligs = rand() % 10;
do{
ligIdx = rand() % deposit.size();
dist = rand() % 100;
deposit.at(i)->addConnection(deposit.at(ligIdx), dist);
ligs--;
}while(ligs>0);
(...)
My deposit class has 2 subclasses. I dont know why the error occurs. Is there any problem with the iterator?
Thank you very much!!!
The problem you have is pretty nasty: getConnections() returns a multimap by value.
This means that successive calls to deposit.at(j)->getConnections() refer to different temporary copies of the original multimap. Thus the the iterator created on the begin of the first temporary copy, will never match the end of the second copy, without first accessing illegally some invalid places.
Two alternatives:
if you want to iterate on a copy, make one local copy auto cnx = deposit.at(j)->getConnections(); and change your inner loop to iterate on cnx.
if you intended to iterate on the original multimap, change the signature of getConnections() to return a reference.
By the way, if you use c++11 or higher, you could consider defining the iterator in a more readable way: for (auto it = ....) or even better, using the range-for syntax as proposed by Norah Attkins in her answer.
If you have a c++11 (or 14) compiler (and you should - unless it's a work/company barrier involved) consider using range based for loops to make your code clearer
for (auto const& elem : deposit)
{
for (auto const& product : elem)
{
}
}
Apart from the stylist guidance, lacking info on what the containers actrually hold, we'd just be guessing what's wrong when answering this question. My guess is that invalid reads happen and the pointers you're accessing are not allocated (but that's a guess)
just a quick one:
I plan to have an array of AVL Trees (for an assignment, as you imagined - does anyone ever use AVL trees apart from data structures students anyway?) and I was wondering if I could use a nice vector - and take advantage of the for(auto i : vect) c++ 11 functionality.
What I want to do: AVLTree array of 1.000.000 elements so I can check in CONSTANT time if the tree exists or not (array position will be NULL or not)
AVLTree_GeeksforGeeks **AVLArray = new (AVLTree_GeeksforGeeks*)[1000000];
for(int i=0; i<1000000; i++){ AVLArray[i] = nullptr; } //init everything to null
//do stuff with AVL trees
//...
if(AVLTree[52000]!=nullptr)
{
cout << "tree exists!\n";
}
Is there an equivalent with vectors, that will allow me constant time of searching for a tree? All the examples I've seen use vector.push_back() and vector.find() to search.
You can use std::vector as suggested by Exceptyon:
std::vector<unique_ptr<AVLTree>> trees(1000000);
by using also the smart pointers implemented in c++11. If your concern is dynamic resizing keep in mind that you can can reserve an initial amount of storage when you create the vector (by passing it as a parameter in the constructor) or via the resize member.
If your concern is random access to its objects, rest assured that the operator[] has O(1) complexity.
If you know the total capacity of the container at compile time you could also consider using c++11's std::array which provides the same for each functionality as well as the same constant time access to its elements.
std::array<unique_ptr<AVLTree>, 1000000> trees;
vector will work because they have an overloaded operator[] that guarantee constant time access to the nth element.
But your code is not clear:
AVLTree_GeeksforGeeks *AVLArray = new AVLTree_GeeksforGeeks[1000000];
for(int i=0; i<1000000; i++){ AVLArray[i] = nullptr; } //init everything to null
If you set to nullptr, then you need a pointer. Is AVLTree_GeeksforGeeks a typedef on a pointer ? I assume it is not the case, and that there is a typo -- otherwise you just have to remove this typedef definition to use std::unique_ptr<TheRealTyp>. So to clarify, I suppose your code is really:
AVLTree_GeeksforGeeks **AVLArray = new (AVLTree_GeeksforGeeks*)[1000000];
for(int i=0; i<1000000; i++){ AVLArray[i] = nullptr; } //init everything to null
In that case, as suggested you should use a std::vector<std::unique_ptr<AVLTree_GeeksforGeeks>>, and you won't have to initialize it to nullptr and the test of nullity changed for a direct "test" of std::unique_ptr:
std::vector<std::unique_ptr<AVLTree_GeeksforGeeks>> AVLArray(100000);
// Do stuff with AVL trees
if (AVLArray[52000])
{
cout << "tree exists!\n";
}
Now, how to use a std::vector<std::unique_ptr<X>> ?
Setting a value in the already allocated zone: AVLArray[5200] = std::unique_ptr(new AVLTree_GeeksforGeeks));
Setting a entry to null: AVLArray[5200].reset()
If you need to add something (the vector will grow): AVLArray.push_back(std::unique_ptr(new AVLTree_GeeksforGeeks));
To iterate over use for (auto& elem: AVLArray). The & is mandatory otherwise a copy construcotr is called and std::unique_ptr forbids this.
Here a example:
#include <iostream>
#include <vector>
#include <memory>
// boost
#include <boost/range/algorithm/for_each.hpp>
#include <boost/range/adaptor/filtered.hpp>
class A {};
int main(int argc, char const *argv[])
{
std::vector<std::unique_ptr<A>> vector;
vector.resize(10000);
// Adding some values
if (!vector[100])
{
std::cout << "Adding vector[100]" << std::endl;
vector[100] = std::unique_ptr<A>(new A);
}
if (!vector[1000])
{
std::cout << "Adding vector[1000]" << std::endl;
vector[1000] = std::unique_ptr<A>(new A);
}
// Removing one
if (vector[100])
{
std::cout << "Removing vector[100]" << std::endl;
vector[100].reset();
}
std::cout << "Testing element." << std::endl;
auto printer = [](const std::unique_ptr<A>& elem) {
std::cout << "There is an elem !" << std::endl; };
// use auto& otherwise use unique_ptr(const unique_ptr&) that has been
// deleted)
for (auto& elem: vector)
{
if (elem)
{
printer(elem);
}
}
std::cout << "for_each element with filtering." << std::endl;
auto is_null = [](const std::unique_ptr<A>& elem) { return (bool) elem; };
// Just because I move boost range !
boost::for_each(vector | boost::adaptors::filtered(is_null), printer);
std::cout << "end !" << std::endl;
return 0;
}
map<int, string>::reverse_iterator& it = temp.rbegin();
it -> points to garbage key value
it++ -> points to the correct key value
map<int, string>::iterator& it = temp.begin();
it-> points to the correct key value from beginning.
Please assist.
Your statements are incorrect. If temp is not empty, then *temp.rbegin() is indeed the last value in the map, and *temp.begin() is the first value.
(However, the underlying iterator of the reverse begin is the ordinary end iterator - but you don't see that unless you call base() on the reverse iterator.)
You must have an error in your code that's filling the map. You can verify this by testing a trivial example such as
#include <algorithm>
#include <map>
#include <iostream>
using namespace std;
int main()
{
map<int, char> coll;
// insert elements from 1 to 9
for (int i=1; i<=9; ++i) {
coll[i] = static_cast<char>(i+'a'-1); // because adding 96 is less obvious that we're indexing based at letter a
}
// print all element in reverse order
for_each (coll.rbegin(), coll.rend(),
[]( pair<int, char> mapinfo ) { cout << mapinfo.second << " "; } );
cout << endl;
}