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
Related
I understand that std::map is (Key, Value) pairs.
I want to search through the values of a map. Let us say that I want to find the highest value among the values in the std::map. How can I do that ?
For example let me consider a map like this:
John -> 100
Jeffrey -> 200
Krishna -> 147
I think it will be similar to this , but I am not sure.
for (auto it=m.begin(); it!=m.end(); it++)
{
if (it->second == 500)
{
cout << "Found";
}
else {
continue;}
}
Instead of iterating through std::map, is there any other inbuilt method using which I can check if a value exists in a std::map with O(1) time complexity ?
Q1: How to check if a value exists in hashmap?
You need to iterate through and check if such item exists. You can use `std::find_if() with a lambda or do that through a loop. If you do that quite often you may want to index values as well (see below)
Q2: How to iterate through all the values in a map and find the largest value or the smallest value ?
Again you iterate through container and find it or you can use std::max_element() or std::min_element() with a lambda as well. Though if you need to access values in sorted order you may consider to use boost::multimap which will allow to access data using hashed index by name and provide sorted or hashed index(es) for values, though you should be aware of a price of every index added.
For the second question, use:
std::map<std::string, int> foo = {{"John",100},{"Jeffrey",200},{"Krishna",147}};
std::cout << std::max_element(foo.begin(), foo.end(), [](const auto& p1, const auto& p2){return p1.second < p2.second;})->first;
std::cout << std::min_element(foo.begin(), foo.end(), [](const auto& p1, const auto& p2){return p1.second < p2.second;})->first;
Use an adapted lambda with std::find_if, you should be able to find also if a value exists in a map (or hash table).
I have a recurrent pattern with the use of std::map.
I want to retrieve the value only when the key is present, otherwise I don't want to insert element. Currently I'm using count(key) or find(key) (which one is better? from the documentation the complexity seems to be the same) and if them returns a positive value that I access the map. However I would like to avoid the use of two operations on the map. Something like:
map<string, int> myMap;
int returnvalue;
boole result = myMap.get("key1",returnValue)
if(result){
\\ use returnValue
}
Reading the std::map documentation on cplusplus.com I found two functions for accessing map elements:
at(): which throws an excpetion if the key is not present
[]: which insert a new value if the key is not present
None of them satisfy my necessity.
Use map::find:
auto it = myMap.find(key);
if (it != myMap.end())
{
// use it->second
}
else
{
// not found
}
This part was easy. The harder problem is when you want to look up if an element exists and return it if it does, but otherwise insert a new element at that key, all without searching the map twice. For that you need to use lower_bound followed by hinted insertion.
using count() for sure the key is exists
then uses find() to get the k/v pair
if (myMap.count(key))
{
auto it = myMap.find(key)
}
else
{
// not found
}
assumend I have a (filled) list
std::list<std::pair<int,otherobject>> myList;
and want to find() the first element within this list, where int has a specific value - how can I do that?
To explain it a bit further:
I want to append these pairs to the list with an int that identifies otherobject but is not unique. The order where these int/otherobject pairs arrive has to be kept.
When an int is found during access to elements of this list the first occurence of that int has to be given back (and removed).
Thanks!
I think I'd use the standard find_if algorithm:
auto pos = std::find_if(myList.begin(), myList.end(),
[value](std::pair<int, otherobject> const &b) {
return b.first == value;
});
That gives an iterator to the element with the required value -- from there, you can copy the value, delete the value, etc., just like with any other iterator.
According to your need the better option would be to use a multimap.
In you case it would give :
std::multimap<int, otherobject> myMultiMap;
Then when looking for otherobjects linked to a int ( myInt) you'll do :
std::pair<std::multimap<int, otherobject>::iterator, std::multimap<int, otherobject>::iterator> result = myMultiMap.equal_range(myInt);
for (std::multimap<int,otherobject>::iterator iter=result.first; iter!=result.second; ++iter)
{
std::cout << it->second;
}
This is a STL container so you'll easilly find online documentation.
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)
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.