C++ map start search from iterator position - c++

My question is the following:
After using find on a std::map to get an iterator pointed to the desired element pair, is it possible to reuse that iterator on subsequent find()'s to take advantage of knowing that the elements im looking for afterwards are close to the first found element? Something like:
std::map<key, value> map_elements;
std::map<key, value>::iterator it;
it = map_elements.find(some_key);
it = it.find(a_close_key)
Thank you in advance

If you're sure it's really nearby, you could use std::find (instead of map::find) to do a linear search for the item. If it's within approximately log(N) items of the current position, this is likely to be a win (where N is the number of items in the map).
Also note that you'll have to figure out whether you want a search before or after the current position, and specify current, end() if it's after, and begin(), current if it's before. If it's before, you'll want to do a reverse search (find_end, if memory serves) since the target is presumably close to the end of that range.

Your question is not complete about how far Item1(found by map::find) can be far from Item2. In some case its more effecient to make new map::find; in some cases you can just iterate your iterator to find where your second item can be. Using just search map::find it will be O(log n) complexity and it can be around 10-20 steps.
So, if you know your Item2 is not so far, you can just iterate it iterator to find it out. Most important thing here is how to check you must stop search. std::map uses std::less<T> by default to arrange items, so it can be used to find out container is not containing Item2 at all. Something like this(not tested):
std::map<key, value> map_elements;
std::map<key, value>::iterator it, it2;
it2 = it = map_elements.find(some_key);
bool found=false;
while( it2!=map_elements.end() && !(a_close_key < it2->first) ) {
if( !(a_close_key < it2->first) && !(it2->first < a_close_key) ) {
//Equivalency is not ==, but its what used in std::map
found=true;
break;
}
it2++;
}
if( found ) {
//.... use it2
}
Inside if( found ) block your iterator it2 value should be same as if you called map_elements.lower_bound(a_close_key)

Related

Iterating over a map, starting at a specific key

I am implementing a function meant to find any strings that share a prefix with a given string. All of the possible strings to compare to are in a map already, and I would like to iterate over that map starting at where the given string is. Currently, I am looping over the entire map, but for performance reasons, I need to make this more efficient. This is how I am currently iterating over the map:
for(auto const& it : lookup_map){ //performs code }
I would like this to not start from the beginning of the map, but wherever the given string is in the map.
Just use good old iterators:
for( auto it = lookup_map.find( your_string ); it != lookup_map.end(); ++it ) {
// using it
}
std::map has its own member function lower_bound for what you want to do. You can use std::map::lower_bound to get your starting position and then do a compare of the prefix for subsequent iterations:
for (auto it = lookup_map.lower_bound(prefix);
it != std::end(lookup_map) && it->first.compare(0, prefix.size(), prefix) == 0;
++it)
{
...
}
This will be more efficient than iterating every single key. The advantage in this case of using lower_bound() is that it will return the first item that is equivalent or after the search term. So if your search term is "aa" and you have an entry "aab" in your map, lower_bound() will return an iterator to "aab". I think this will be more useful in your case because you want to search on the prefix.
In C++20, std::string has a starts_with() function. So we can use this function to check the prefix and simplify our code a little:
for (auto it = lookup_map.lower_bound(prefix);
it != lookup_map.end() && it->first.starts_with(prefix); ++it)
{
...
}
Demo

How to get the iterator (or the value) for "previous" item for given key of std::multimap?

I would like to get the item that goes before given key, for my std::multimap.
For the item that goes after given key I could simply use std::multimap::upper_bound (it will return the element with key greater then given). But unfortunately, std::multimap::lower_bound returns the element with the "lower or equal" key.
The sample taken from http://www.cplusplus.com/reference/map/multimap/lower_bound/:
mymultimap.insert(std::make_pair('a',10));
mymultimap.insert(std::make_pair('b',121));
mymultimap.insert(std::make_pair('c',1001));
mymultimap.insert(std::make_pair('c',2002));
mymultimap.insert(std::make_pair('d',11011));
mymultimap.insert(std::make_pair('e',44));
itlow = mymultimap.lower_bound ('b'); // itlow points to b
itup = mymultimap.upper_bound ('d'); // itup points to e (not d)
How to get the iterator (or the value) for a when you give b as a parameter?
You can use lower_bound, but there are two edge cases you have to consider: it returns begin() and it returns end():
auto itfound = mymultimap.lower_bound('b');
if (itfound == mymultimap.begin()) {
// 'b', or something past 'b', is the first item
// or the map is empty
// what to do here?
}
else if (itfound == mymultimap.end()) {
// there does not exist an item >= 'b'
// what to do here? possibly std::prev(end()) ?
}
else {
// ok cool, we found something in the middle
// just back up
--itfound;
// do stuff with itfound here
}
Go ahead and use lower_bound, then decrement the iterator you receive (after checking to make sure it isn't begin).
itfound = mymultimap.lower_bound('b');
if (itfound != mymultimap.begin())
{
--itfound;
// do something with itfound
}
You may need slightly different logic if the item isn't in the map, but that shouldn't be a hard modification.
We know the following that for any sequenced container (i.e. map, set, multimap and multiset):
lower_bound(key) returns the first entry >= key
upper_bound(key) returns the first entry > key
Each function also returns end() in the event there is no entry >= / > key.
So we have functionality based on the operators > and >=. We might reasonably ask, can we have functionality based on < and <= as well?
It is somewhat trivial to ask for the first entry < or <= key (this is either begin() or not in the container). However it is meaningful to ask for is the last such entry. Indeed, what you are asking for is the last entry < key.
For completeness we will consider the last entry <= key too.
Let's suppose we have such functions, what should they do if there is no such entry? We could do a lot worse than return end() too - it is a testable value and distinct from any successful case. It is also consistent with both lower_bound, upper_bound, as well as find and a whole host of standalone search functions in the <algorithm> header. The bottom line is that end() is routinely used to mean `no such entry' so this should be good enough for us as well.
As noted by Barry and Mark, calling lower_bound(key) and then decrementing will give us what we want, unless lower_bound(key) == begin(). In this case, the first (and thus [joint-]smallest) entry is >= key, so there are no entries < key - precisely the scenario where we just said we should return end().
For the case of looking for the last entry <= key we can do the same but with upper_bound, again returning end() to mean 'no such entry' when upper_bound(key) == begin().
Lets call these functions lower_bound_dec and upper_bound_dec respectively:
// Find the last entry < key, returning end() if all entries are >= key
// Container can be any that implements lower_bound, e.g. std::map, std::multimap, std::set or std::multiset
template<typename Container, typename Key>
typename Container::const_iterator lower_bound_dec(const Container &container, const Key &key) {
auto it = container.lower_bound(key);
if (it == std::begin(container))
it = std::end(container);
else
--it;
return it;
}
// Find the last entry <= key, returning end() if all entries are > key
// Container can be any that implements upper_bound, e.g. std::map, std::multimap, std::set or std::multiset
template<typename Container, typename Key>
typename Container::const_iterator upper_bound_dec(const Container &container, const Key &key) {
auto it = container.upper_bound(key);
if (it == std::begin(container))
it = std::end(container);
else
--it;
return it;
}
We might also define iterator based functions, that mirror std::lower_bound and std::upper_bound and can operate on any sorted range with bidirectional iterators:
// Find the last entry < key, returning end() if all entries are >= key
// last must be reachable from first, and all entries in sorted order between them
template<typename Iterator, typename Key>
Iterator lower_bound_dec(Iterator first, Iterator last, const Key &key) {
auto it = container.lower_bound(first, last, key);
if (it == first)
it = last;
else
--it; // requires bidirectional iterators
return it;
}
upper_bound(first, last, key) and the forms that take a comparator left as exercises to the reader / anyone who wants to earn rep by editing! :)
TL/DR:
Use lower_bound and then decrement, with the caveat that "decrementing" begin() yields end() to mean 'no such entry.'

How to get the last element of an std::unordered_map?

How to get the last element of an std::unordered_map?
myMap.rbegin() and --myMap.end() are not possible.
There is no "last element" in a container that is unordered.
You might want an ordered container, e.g. std::map and access the last element with mymap.rbegin()->first (Also see this post)
EDIT:
To check if your iterator is going to hit the end, simply increment it (and possibly save it in a temporary) and check it against mymap.end(), or, even cleaner : if (std::next(it) == last)
In your comments, it appears your goal is to determine if you are on the last element when iterating forward. This is a far easier problem to solve than finding the last element:
template<class Range, class Iterator>
bool is_last_element_of( Range const& r, Iterator&& it ) {
using std::end;
if (it == end(r)) return false;
if (std::next(std::forward<Iterator>(it)) == end(r)) return true;
return false;
}
the above should work on any iterable Range (including arrays, std containers, or custom containers).
We check if we are end (in which case, we aren't the last element, and advancing would be illegal).
If we aren't end, we see if std::next of us is end. If so, we are the last element.
Otherwise, we are not.
This will not work on iterators that do not support multiple passes.
You cant. by definition, the element is not stored based on some sort of order. the key is hashed first and that's why O(1) search is possible. if you wanna check whether a key exists in the unordered_map or not, u can use this code:
std::unordered_map dico;
if(dico.count(key)!=0){
//code here
}
std::unordered_map::iterator last_elem;
for (std::unordered_map::iterator iter = myMap.begin(); iter != myMap.end(); iter++)
last_elem = iter;
// use last_elem, which now points to the last element in the map
This will give you the last element in whatever order the map gives them to you.
Edit: You need to use std::unordered_map<YourKeyType, YourValueType> instead of just std::unordered_map. I just wrote it like this because you did not provide the type in your question.
Alternatively, as suggested by vsoftco (thanks), you could declare both last_elem and iter as decltype(myMap)::iterator.
(If you're compiling with the MSVC++ compiler, then you will need to add typedef decltype(myMap) map_type; and then instead of decltype(myMap)::iterator use map_type::iterator.)
.end() is an iterator to the "element past the last element". That's why you compare it like this when you loop through a map:
for (auto it = myMap.begin(); it != myMap.end(); ++it) // '!=' operator here makes it possible to only work with valid elements
{
}
So you want the "last" element (whatever that may be, because it's not really guaranteed to be the last in an unordered map, since it ultimately depends on how the key was hashed and in which "bucket" it ends up in). Then you need: --myMap.end()
More specifically, .end() is a function, that returns an iterator, same as .begin() returns an iterator. Since there is no .rbegin() in an std::unordered_map, you have to use -- (the decrement operator):
auto it = --myMap.end();
To access the key you use it->first, to access the value you use it->second.
The accepted answer seems wrong. Unordered_map does have the last element even though the key-value pair is not stored in sorted order. Since the iterator of unorered_map is forwar_iterator(LegacyForwardIterator), the cost to find the last element is O(n). Yakk - Adam gave the correct answer. Essentially, you have to iterator the container from begin to end. At each iteration, you have to check whether the next element is end(); if yes then you are at the last element.
You cannot call prev(it) or --it. There will be no syntax error, but you will have a runtime error (more likely segmentation fault) when using the prev(it) or --it. Maybe next version of compiler can tell you that you have an logic error.
It may not be the best solution, performance-wise, but in C++11 and later, I use a combination of std::next() and size() to jump all elements from the beginning of the map, as shown below:
std::unordered_map<int,std::string> mapX;
...
if (mapX.size() > 0) {
std::unordered_map<int,std::string>::iterator itLast =
std::next(mapX.begin(), mapX.size() - 1);
...

Get all the keys which matches a query in a map

Say I have more than one key with the same value in a map. Then in that case how do I retrieve all keys that matches a query.
Or, Is there any possibility to tell find operation to search after a specific value.
I am using an std::map, C++.
Would something like this work for you:
void FindKeysWithValue(Value aValue, list<Key>& aList)
{
aList.clear();
for_each(iMap.begin(), iMap.end(), [&] (const pair<Key, Value>& aPair)
{
if (aPair.second == aValue)
{
aList.push_back(aPair.first);
}
});
}
The associative containers probably won't help you too much because for std::map<K, V> the key happens to be unique and chances that your chosen query matches the ordering relation you used may not be too high. If the order matches, you can use the std::map<K, V> members lower_bound() and upper_bound(). For std::multimap<K, V> you can also use equal_range().
In general, i.e., if you query isn't really related to the order, you can use std::copy_if() to get a sequence of objects matching a predicate:
Other other;
// ...
std::vector<Other::value_type> matches;
std::copy_if(other.begin(), other.end(),
std::back_inserter(matches), predicate);
When copying the elements is too expensive, you should probably consider using std:find_if() instead:
for (auto it(other.begin());
other.end() != (it = std::find_if(it, other.end(), predicate));
++it) {
// do something with it
}
The only way is to iterate over map.
this link may be useful: Reverse map lookup
Provided you want quick access and you don't mind using some more space, then you maintain another map that gets stored as value, key. In your case, you would need to handle the duplicate values (that you will be storing as keys).
Not a great idea but definitely an option.
A map is meant for efficient lookup of keys. Lookup based on values is not efficient, and you basically have to iterate through the map, extracting matches yourself:
for(map<A,B>::iterator i = m.begin(); i != m.end(); i++)
if(i->second == foo)
you_found_a_match();
If you intend to do this often, you can build up a multimap mapping the other way, so you can efficiently perform a value-based lookup:
multimap<B,A> reverse;
for(map<A,B>::iterator i = m.begin(); i != m.end(); i++)
reverse.insert(pair<B,A>(i->second,i->first));
You can now easily find the keys with a given value value:
matches = reverse.equal_range(value);
for(multimap<B,A>::iterator i = matches.first; i != matches.second; i++)
A & key = i->second;
If these maps aren't going to grow continuously, it may be more efficient to simply maintain a vector > instead, define a comparator for it based on the value, and use equal_range on that instead.

finding a key in a map

I have a map which I have declared as follows:
map<int, bool> index;
and I insert values into the map as:
int x; cin>>x;
index[x]=true;
However,
cout<<index[y]; // for any number y not inindexgives me 0
As I get the value 0 when I check for a key which is not present in the map, how can I reliably find out if a key is present in the map or not?
I'm using a map for trying to find out if two sets are disjoint or not, and for the same I am using a map, and two vectors to store the input. Is this shabby in any way? Some other data structure I should be using?
You can use if (index.find(key) == index.end()) to determine if a key is present. Using index[key] you default-construct a new value (in this case, you call bool(), and it gets printed as 0.) The newly constructed value also gets inserted into the map (i.e. index[key] is equal in this case to index.insert(std::make_pair(key, bool()).)
Using two data structures for the same data is ok. However, is there any need to use a map, wouldn't a set suffice in your use case? I.e. if they key is presents, the value is true, and false otherwise?
To find if two sets (given as std::set) are disjoint, you can simply compute their intersection:
std::set<T> X, Y; // populate
std::set<T> I;
std::set_difference(X.begin(), X.end(), y.begin(), y.end(), std::back_inserter(I));
const bool disjoint = I.empty();
If your containers aren't std::sets, you have to make sure the ranges are ordered.
If you want to be more efficient, you can implement the algorithm for set_intersection and stop once you have a common element:
template <typename Iter1, typename Iter2>
bool disjoint(Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2)
{
while (first1 != last1 && first2 != last2)
{
if (*first1 < *first2) ++first1;
else if (*first2 < *first1) ++first2;
else { return false; }
}
return true;
}
Use map::find.
you can use index.find(key) != index.end() or index.count(key) > 0
Depending on the range of index items, it might be good to use a bitmap (only makes sense for a reasonnably small range of possible index items. Will make checks for being disjoint super easy and efficient) or use a a set instead of a map (map stores additional bools that are not really needed). A set also offers methods count(key) and find(key)
1, Use index.count(y). It's more concise than and equivalent to index.find(y) != index.end(), except for the fact that it's an integer 1 or 0, whereas of course != gives you a bool.
The downside is that count is potentially less efficient for multimap than it is for map, since it may have to count more than one entry. Since you aren't using a multimap, no problem.
2, You could sort both vectors and use std::set_intersection, but it's not a perfect fit if all you care is whether the intersection is empty or not. Depending where the input comes from, you may be able to get rid of both vectors and just construct a map as you go from the first load of input, then check each element of the second load of input against it. Finally, use a set instead of a map.