At the moment my solution is to iterate through the map to solve this.
I see there is a upper_bound method which can make this loop faster, but is there a quicker or more succinct way?
The end:
m.rbegin();
Maps (and sets) are sorted, so the first element is the smallest, and the last element is the largest. By default maps use std::less, but you can switch the comparer and this would of course change the position of the largest element. (For example, using std::greater would place it at begin().
Keep in mind rbegin returns an iterator. To get the actual key, use m.rbegin()->first. You might wrap it up into a function for clarity, though I'm not sure if it's worth it:
template <typename T>
inline const typename T::key_type& last_key(const T& pMap)
{
return pMap.rbegin()->first;
}
typedef std::map</* types */> map_type;
map_type myMap;
// populate
map_type::key_type k = last_key(myMap);
The entries in a std::map are sorted, so for a std::map m (assuming m.empty() is false), you can get the biggest key easily: (--m.end())->first
As std::map is assosiative array one can easily find biggest or smallest key very easily. By defualt compare function is less(<) operator so biggest key will be last element in map. Similarly if someone has different requirement anyone can modify compare function while declaring map.
std::map< key, Value, compare< key,Value > >
By default compare=std::less
Since you're not using unordered_map, your keys should be in order. Depending upon what you want to do with an iterator, you have two options:
If you want a forwards-iterator then you can use std::prev(myMap.end()). Note that --myMap.end() isn't guaranteed to work in all scenarios, so I'd usually avoid it.
If you want to iterate in reverse then use myMap.rbegin()
Since the map is just an AVL tree then, it's sorted -in an ascending order-. So, the element with largest key is the last element and you can obtain it using one of the following two methods:
1.
largestElement = (myMap.rbegin())-> first; // rbegin(): returns an iterator pointing to the last element
largestElement = (--myMap.end())->first; // end(): returns an iterator pointing to the theortical element following the last element
Related
I need suggestion to use STL containers in the best possible way to sort 3 sets of data
1. A ID (Integer)
2. First Value (String)
3. Second Value (String)
An example of the data structure is as below:
I want to use map as it is sorted at the time of insert and no need to execute a sorting algorithm separately. Since the ID can repeat it must be a multimap, and each data of a column is linked to each other so the rows will change in order to sort keeping the same values attached to a ID.
Sorting the ID and value is ok, but how do I sort 2 values as multimap can take only one value. From my thinking it will be multimap of a multimap or a struct of the data structure and then STL containers. But I want to make it as simple as possible. I need suggestion on how this can be achieved.
Thanks!
Having a map or a set makes sense if and only if you are going to do many insert/erase operations it. If the data structure is static, storing a vector and sorting it once is way more effective. That said, if I understand your question correctly, I think you don't need a map, but a multiset.
typedef pair<int, pair<string, string>> myDataType;
set<myDataType> mySet;
here, the operator < of pair will take care of the ordering.
If you don't want to refer to the id as elem.first, and to the strings as elem.second.first, and elem.second.second, then you can use a struct and overload operator < for it.
struct myType
{
int id;
string s1;
string s2;
};
bool operator < (const myType& t1, const myType& t2)
{
return make_tuple(t1.id, t1.s1, t1.s2) < make_tuple(t2.id, t2.s1, t2.s2);
}
You could just use a std::set<std::tuple<int, std::string, std::string>>. Tuples are lexicographically compared thus you would get the effect you want for free.
Live Demo
Elements in a multimap are sorted by the Key. You cannot 'sort' multimap. What you can do is to create vector of pairs<Key, Map<Key>::Interator> with elements fulfilling some logical condition and sort vector.
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
If I have a map with keys as strings. How can I know which string is in which place in the map? For example, 'cats' is the first entry and 'dogs' are the second entry. How will I know that dogs are the second entry in the map? Should I go through the map and search/check the order or is there any easier way?
You can find() the element, and then compute the distance between the element and the map's begin(). Since this is a map, the complexity of distance will be O(n) with the number of elements in the map. I doubt you'd see much performance improvement versus simply looping the map and counting, but I'd prefer to not write such hand-written loops.
But this smells like an XY Problem. Why would you need the position of an element in a map?
You can't do that anyway better, and here's two reasons for that:
a) std::map has a tree inside that, so initially leaves of tree don't have linear order, only a partial order (of course you can compare two keys, but you can't do that for a general binary tree - that's the reason for complication).
b) std::map has bidirectional iterator, so you can't have effective iterator arithmetics. You could do that like this:
typedef map<string, int> MyMapT;
MyMapT data;
// ...
auto it = data.find("dog");
if (it == data.end()) {
// don't have such a key
}
auto position = distance(data.begin(), data.end());
Unfortunately, that's not the best way to do that, because distance function works in O(n) time for bidirectional iterators. Again, that's because of tree structure inside the std::map.
map <int, string> rollCallRegister;
map <int, string> :: iterator rollCallRegisterIter;
map <int, string> :: iterator temporaryRollCallRegisterIter;
rollCallRegisterIter = rollCallRegister.begin ();
tempRollCallRegisterIter = rollCallRegister.insert (rollCallRegisterIter, pair <int, string> (55, "swati"));
rollCallRegisterIter++;
tempRollCallRegisterIter = rollCallRegister.insert (rollCallRegisterIter, pair <int, string> (44, "shweta"));
rollCallRegisterIter++;
tempRollCallRegisterIter = rollCallRegister.insert (rollCallRegisterIter, pair <int, string> (33, "sindhu"));
// Displaying contents of this map.
cout << "\n\nrollCallRegister contains:\n";
for (rollCallRegisterIter = rollCallRegister.begin(); rollCallRegisterIter != rollCallRegister.end(); ++rollCallRegisterIter)
{
cout << (*rollCallRegisterIter).first << " => " << (*rollCallRegisterIter).second << endl;
}
Output:
rollCallRegister contains:
33 => sindhu
44 => shweta
55 => swati
I have incremented the iterator. Why is it still getting sorted? And if the position is supposed to be changed by the map on its own, then what's the purpose of providing an iterator?
Because std::map is a sorted associative container.
In a map, the key value is generally used to uniquely identify the element, while the mapped value is some sort of value associated to this key.
According to here position parameter is
the position of the first element to be compared for the insertion
operation. Notice that this does not force the new element to be in
that position within the map container (elements in a set always
follow a specific ordering), but this is actually an indication of a
possible insertion position in the container that, if set to the
element that precedes the actual location where the element is
inserted, makes for a very efficient insertion operation. iterator is
a member type, defined as a bidirectional iterator type.
So the purpose of this parameter is mainly slightly increasing the insertion speed by narrowing the range of elements.
You can use std::vector<std::pair<int,std::string>> if the order of insertion is important.
The interface is indeed slightly confusing, because it looks very much like std::vector<int>::insert (for example) and yet does not produce the same effect...
For associative containers, such as set, map and the new unordered_set and co, you completely relinquish the control over the order of the elements (as seen by iterating over the container). In exchange for this loss of control, you gain efficient look-up.
It would not make sense to suddenly give you control over the insertion, as it would let you break invariants of the container, and you would lose the efficient look-up that is the reason to use such containers in the first place.
And thus insert(It position, value_type&& value) does not insert at said position...
However this gives us some room for optimization: when inserting an element in an associative container, a look-up need to be performed to locate where to insert this element. By letting you specify a hint, you are given an opportunity to help the container speed up the process.
This can be illustrated for a simple example: suppose that you receive elements already sorted by way of some interface, it would be wasteful not to use this information!
template <typename Key, typename Value, typename InputStream>
void insert(std::map<Key, Value>& m, InputStream& s) {
typename std::map<Key, Value>::iterator it = m.begin();
for (; s; ++s) {
it = m.insert(it, *s).first;
}
}
Some of the items might not be well sorted, but it does not matter, if two consecutive items are in the right order, then we will gain, otherwise... we'll just perform as usual.
The map is always sorted, but you give a "hint" as to where the element may go as an optimisation.
The insertion is O(log N) but if you are able to successfully tell the container where it goes, it is constant time.
Thus if you are creating a large container of already-sorted values, then each value will get inserted at the end, although the tree will need rebalancing quite a few times.
As sad_man says, it's associative. If you set a value with an existing key, then you overwrite the previous value.
Now the iterators are necessary because you don't know what the keys are, usually.
Since there is no .resize() member function in C++ std::map I was wondering, how one can get a std::map with at most n elements.
The obvious solution is to create a loop from 0 to n and use the nth iterator as the first parameter for std::erase().
I was wondering if there is any solution that does not need the loop (at least not in my user code) and is more "the STL way to go".
You can use std::advance( iter, numberofsteps ) for that.
Universal solution for almost any container, such as std::list, std::map, boost::multi_index. You must check the size of your map only.
template<class It>
It myadvance(It it, size_t n) {
std::advance(it, n);
return it;
}
template<class Cont>
void resize_container(Cont & cont, size_t n) {
cont.erase(myadvance(cont.begin(), std::min(n, cont.size())),
cont.end());
}
The correct way for this is to use std::advance. But here is a funny (slow) way allowing to 'use resize on map'. More generally, this kind of trick can be used for other things working on vector but not on map.
map<K,V> m; //your map
vector< pair<K,V> > v(m.begin(), m.end());
v.resize(n);
m = map<K,V>(v.begin(),v.end());
Why would you want to resize a map?
The elements in a map aren't stored in any order - the first 'n' doesn't really mean anything
edit:
Interestingly std::map does have an order, not sure how useful this concept is.
Are the entries in the same sort order as the keys?
What does that mean? If you have Names keyed by SSN does that mean the names are stored in SSN numeric order?
A std::map is not a list. There are no "first n" elements.
BTW: Iterators become invalid if the container is changed.
If you really need a smaller map you could iterate though it and add all elements up to the n-th into a new map.