So i'm fairly new to c++ and i'm a little confused on how to implement the find() function on my set which is storing a pair item. Ive read on how to insert and remove items by their pair but came up dry on anyone explaining how to use find (Or some other method, if there is one) to find a value by the first item of the pair.
set<pair<string, CustomObject>> *items = new set<pair<string, CustomObject>>();
Then lets say I insert a few pairs into the set then I want to find one of those pair by searching for the "key" being stored as the first item in the pair. I think it would involve calling the .first on the pair but im just having trouble with that. This is the basic function im trying to implement
bool inSet(string key){
return this->items->find(pair<string, CustomObject>(key, null).first)
}
I was able to implement everything just fine in a map object but then I had to switch to a set because I wanted to be able to sort the items in the data structure and I was told that you cant efficiently do this in a map, hence the set.
std::set stores and searches for values based on the entire value. So when you do a find for pair(key, null), and the set contains pair(key, somevalue), it won't find it, as they are not the same.
If you want to search by just the key, you need a std::map. As you say, that doesn't do any searching or sorting by the value, so you can only have one entry with a given key.
If you want to search/sort by both just the key and the key,value pair (different searches at different points in the lifetime of the same data structure), then you'll need a more complex arrangement.
a map of sets can do what you want:
std::map<string, std::set<CustomObject>> items;
Now when you just want to look up things by key, you just lookup in the map, getting back a set of all the values with that key. If you want to search further for a specific value, you look it up in that set.
To find key in std::set by key stored in pair you need to redefine order comparision procedure for your set (if you need multiple objects use multiset):
typedef pair<string, CustomObject> SetValue
struct CustomObjectCompare {
bool operator() (const SetValue& lhs, const SetValue& rhs) const{
return rhs.first < rhs.first;
}
};
// use multiset insead of set if you need multiple objects per one key
typedef set<pair<string, CustomObject>, CustomObjectCompare> Set;
Set mySet;
bool inSet(string key){
static CustomObject emptyObject;
return mySet.end() != mySet.find(SetValue(key, emptyObject))
}
This example define comparision object CustomObjectCompare and special set class Set with that comparision object. As search as sorting will be only by string. The function isSet search by string and emptyObject is ignored and may be any of existed object. In example it is an function internal once initialized static object.
Related
I have a Java program that I want to convert it to C++. So, there is a Linkedhashmap data structure used in the Java code and I want to convert it to C++. Is there an equivalent datatype for LinkedHashmap in C++?
I tried to use std::unordered_map, however, it does not maintain the order of the insertion.
C++ does not offer a collection template with the behavior that would mimic Java's LinkedHashMap<K,V>, so you would need to maintain the order separately from the mapping.
This can be achieved by keeping the data in a std::list<std::pair<K,V>>, and keeping a separate std::unordered_map<k,std::list::iterator<std::pair<K,V>>> map for quick look-up of the item by key:
On adding an item, add the corresponding key/value pair to the end of the list, and map the key to the iterator std::prev(list.end()).
On removing an item by key, look up its iterator, remove it from the list, and then remove the mapping.
On replacing an item, look up list iterator from the unordered map first, and then replace its content with a new key-value pair.
On iterating the values, simply iterate std::list<std::pair<K,V>>.
The insertion order contract on key iteration can be achieved with a balanced tree for log(n) performance. This is better than maintaining keys in a list as item removal requires n lookup time. My mantra is never put something you look up in a list. If it doesn't have to be sorted, use a hash. If it should be sorted, use a balanced tree. If all you're going to do is iterate, then a list is fine.
In c++ this would be std::map where the key is the item reference and the value is the insertion order, the keys are sorted using red-black trees. See: Is there a sorted container in STL
This is how I do it:
map<TKey, set<MyClass<K1,K2>, greater<MyClass<K1, K2>>>> _objects; // set ordered by timestamp. Does not guarantee uniqueness based on K1 and K2.
map<TKey, map<K2, typename set<MyClass<K1, K2>, greater<MyClass<K1, K2>>>::iterator>> _objectsMap; // Used to locate object in _objects
To add object id:
if (_objectsMap[userId].find(id) == _objectsMap[userId].end())
_objectsMap[userId][id] = _objects[userId].emplace(userId, id).first;
To erase an object id:
if (_objectsMap[userId].find(id) != _objectsMap[userId].end()) {
_objects[userId].erase(_objectsMap[userId][id]);
_objectsMap[userId].erase(id);
}
To retrieve, say the most recent size objects from the list starting from a specific object id:
vector<K2> result;
if (_objectsMap[userId].find(id) != _objectsMap[userId].end() && _objectsMap[userId][id] != _objects[userId].begin()) {
set<MyClass<K2, K2>, greater<MyClass<K1, K2>>>::iterator start = _objects[userId].begin(), end = _objectsMap[userId][id];
size_t counts = distance(_objects[userId].begin(), _objectsMap[userId][id]);
if (counts > size)
advance(start, counts - size);
transform(start,
end,
back_inserter(result),
[](const MyClass<K1, K2>& obj) { return obj.ID(); });
}
return result;
I have nested map of type:
std::map<int,std::map<pointer,pointer>>
I am iterating over the map each time/per frame and doing updates on it.So basically I have 2 nested if loops.
i have an array and i need to sort the data with 2 attributes. First attribute is integer which is the first key, then second attribute is a pointer which is a key of nested map inside the main map. so my code is something like:
iterator = outermap.find();
if(iterator!=outermap.end()){
value = iterator->second;
it1 = value.find();
if(it1!=value.end(){
value1 = it1->second;
// do something
}
else{
// do something and add new value
}
}
else {
// do something and add the values
}
This is really slow and causing my application to drop frame rate. Is there any alternative to this? Can we use hash codes and linked list to achieve the same?
You can use std::unordered_map, it will hash the keys so finds complete faster. Using value = iterator->second is copying your entire map to the 'value' variable. Using a reference avoids unnecessary copying and is better for performance, eg: auto & value = iterator->second.
Also std::map is guaranteed to be ordered. This can be used to your advantage since your keys are integers for the outermost map.
Firstly, your question is a bit vague, so this may or may not fit your problem.
Now, you have a map<int, map<pointer, pointer>>, but you never operate on the inner map itself. All you do is look up a value by an int and a pointer. This is also exactly what you should do instead, use an aggregate of those two as key in a map. The type for that is pair<int, pointer>, the map then becomes a map<pair<int, pointer>, pointer>.
One more note: You seem to know the keys to search in the map in advance. If the check whether the element exists is not just for safety, you could also use the overloaded operator[] of the map. The lookup then becomes outermap[ikey][pkey] and returns a default-initialized pointer (so probably a null pointer, it pointer really is a pointer). For the suggested combined map, the lookup would be outermap[make_pair(ikey, pkey)].
What is an idiomatic way in C++ to have an object store that can be searched with respect to two keys? Essentially what I would like is to store things of type A in a binary search tree (BST) with the BST constructed using the order relation on A.key. However, each A also has a unique A.otherval and I essentially need to delete keys based on this value.
In C I would typically just have a BST with parent pointers and a hash table based with the other values as a key storing pointers to nodes of the BST. I can delete keys through the hash table by getting the node and calling tree delete on that node.
I'm looking for how to do this correctly using STL containers.
If I got the question correctly, all you need is a map in a map, so
std::map<first_key_type, std::map<second_key_type, value_type>> map;
map[key1][key2] = something;
Edit:
I assume that all the values that have the same first key are the same, and the second key is only used as an additional search/remove criteria. In that case, to get the value by the first key only you can use something like
map.at(key).cbegin()->second;
I would recommend two maps, one to map the key to the instance of A (the primary map), and another to map the otherVal to the key:
typedef ... Key;
typedef ... OtherVal;
struct A { Key key; OtherVal otherVal; ... };
typedef std::map<Key,A> KeyToAMap;
typedef std::map<OtherVal,Key> OtherValToKeyMap;
KeyToAMap keyToAMap;
OtherValToKeyMap otherValToKeyMap;
This way you can work with keyToAMap without any additional complexity, but when it comes time to delete, you just need an additional lookup.
To ease the usage, I would also recommend writing functions for wrapping insertion and deletion in both maps:
void insertNewA(const A& a) {
keyToAMap.insert(std::make_pair(a.key, a ));
otherValToKeyMap.insert(std::make_pair(a.otherVal, a.key ));
}
void deleteByOtherVal(const OtherVal& otherVal) {
OtherValToKeyMap::iterator it1 = otherValToKeyMap.find(otherVal);
if (it1 == otherValToKeyMap.end()) { /* error */ }
Key& key = it1->second;
KeyToAMap::iterator it2 = keyToAMap.find(key);
if (it2 == keyToAMap.end()) { /* error */ }
keyToAMap.erase(it2);
otherValToKeyMap.erase(it1);
}
An advantage of this solution is it only requires two maps, as opposed to a multi-level map solution, which requires 1+N maps, where N is the number of entries in the primary map.
i want to access a QMap by it's value, but i don't want to iterate over it and find element with same value and use it's key,
is there anyway to find QMap key by it's content?
my code is :
QMap<int, QVector<QString> >::iterator it;
QMap <QString, int> m_all_data;
i want to access to element of m_all_data with value of my iterator key;
You can use:
const Key QMap::key ( const T & value ) const
which returns the first key with value value or
QList<Key> QMap::keys ( const T & value ) const
which returns a list containing all the keys in the map in ascending order.
But it is slow (linear time), because QMap's internal data structure is optimized for fast lookup by key, not by value.
You can use QMap::values (http://qt-project.org/doc/qt-4.8/qmap.html#values) to get the values.
This will give you a QList on which you can iterate through, and for the given value you can get the key using QMap::key (http://qt-project.org/doc/qt-4.8/qmap.html#key) to get the key for the given value.
You could maintain a second QMap with iterators pointing into the first map, i.e.
QMap<QString, int> m_all_data;
QMap<int, QList<QMap<QString, int>::iterator> > m_data_by_int;
Note that each int value would map to list of iterators since there may be multiple map entries with the same value. Using iterators avoids that you duplicate the QString data. You'd have to make sure to keep the two maps syncronized though, so it might be best to wrap the two maps into a 'bidirectional map' class.
In a C++ std::map, is there any way to search for the key given the mapped value? Example:
I have this map:
map<int,string> myMap;
myMap[0] = "foo";
Is there any way that I can find the corresponding int, given the value "foo"?
cout << myMap.some_function("foo") <<endl;
Output: 0
std::map doesn't provide a (fast) way to find the key of a given value.
What you want is often called a "bijective map", or short "bimap". Boost has such a data structure. This is typically implemented by using two index trees "glued" together (where std::map has only one for the keys). Boost also provides the more general multi index with similar use cases.
If you don't want to use Boost, if storage is not a big problem, and if you can affort the extra code effort, you can simply use two maps and glue them together manually:
std::map<int, string> myMapForward;
std::map<string, int> myMapBackward; // maybe even std::set
// insertion becomes:
myMapForward.insert(std::make_pair(0, "foo"));
myMapBackward.insert(std::make_pair("foo", 0));
// forward lookup becomes:
myMapForwar[0];
// backward lookup becomes:
myMapBackward["foo"];
Of course you can wrap those two maps in a class and provide some useful interface, but this might be a bit overkill, and using two maps with the same content is not an optional solution anyways. As commented below, exception safety is also a problem of this solution. But in many applications it's already enough to simply add another reverse map.
Please note that since std::map stores unique keys, this approach will support backward lookup only for unique values, as collisions in the value space of the forward map correspond to collisions in the key space of the backward map.
No, not directly.
One option is to examine each value in the map until you find what you are looking for. This, obviously, will be O(n).
In order to do this you could just write a for() loop, or you could use std::find_if(). In order to use find_if(), you'll need to create a predicate. In C++11, this might be a lambda:
typedef std::map <unsigned, Student> MyMap;
MyMap myMap;
// ...
const string targetName = "Jones";
find_if (myMap.begin(), myMap.end(), [&targetName] (const MyMap::value_type& test)
{
if (test.second.mName == targetName)
return true;
});
If you're using C++03, then this could be a functor:
struct MatchName
: public std::unary_function <bool, MyMap::value_type>
{
MatchName (const std::string& target) : mTarget (target) {}
bool operator() (const MyMap::value_type& test) const
{
if (test.second.mName == mTarget)
return true;
return false;
}
private:
const std::string mTarget;
};
// ...
find_if (myMap.begin(), myMap.end(), MatchName (target));
Another option is to build an index. The index would likely be another map, where the key is whatever values you want to find and the value is some kind of index back to the main map.
Suppose your main map contains Student objects which consist of a name and some other stuff, and the key in this map is the Student ID, an integer. If you want to find the student with a particular last name, you could build an indexing map where the key is a last name (probably want to use multimap here), and the value is the student ID. You can then index back in to the main map to get the remainder of the Student's attributes.
There are challenges with the second approach. You must keep the main map and the index (or indicies) synchronized when you add or remove elements. You must make sure the index you choose as the value in the index is not something that may change, like a pointer. If you are multithreading, then you have to give a think to how both the map and index will be protected without introducing deadlocks or race conditions.
The only way to accomplish this that I can think of is to iterate through it. This is most likely not what you want, but it's the best shot I can think of. Good luck!
No, You can not do this. You simply have to iterate over map and match each value with the item to be matched and return the corresponding key and it will cost you high time complexity equal to O(n).
You can achieve this by iterating which will take O(n) time. Or you can store the reverse map which will take O(n) space.
By iterating:
std::map<int, string> fmap;
for (std::map<int,string>::iterator it=fmap.begin(); it!=fmap.end(); ++it)
if (strcmp(it->second,"foo"))
break;
By storing reverse map:
std::map<int, string> fmap;
std::map<string, int> bmap;
fmap.insert(std::make_pair(0, "foo"));
bmap.insert(std::make_pair("foo", 0));
fmap[0]; // original map lookup
bmap["foo"]; //reverse map lookup