I've been wondering if it's possible to iterate through part of a map, based on a starting key that may or may not exist. If I had a map with keys full of positions, I would like to say,
"return an iterator such that if _Key doesn't exist, I am returned the closest iterator before _Key"
I could use this to emplace_hint a new value, or loop through a specific range of positions even if the search key does not exist. Is this kind of functionality easily available to us in some way?
Use lower_bound and then decrease iterator, after checking it doesn't point to begin iterator, as Andre Kostur said.
It returns an iterator pointing to the first element in the container whose key is not considered to go before k (i.e., either it is equivalent or goes after). Complexity O(log n).
Related
I understand why an unordered_map has an iterator (begin() and end()) but I don't see what value returning an iterator from find / emplace is.
I can't imagine a use case where you would want to start your iterator at a given key as opposed to begin / end (because a good hash function should make the position of a key in the table arbitrary).
The alternative; to just return the value, reference to the value, or pair of key and value covers all use cases I can imagine an avoids the unnecessary work of creation an iterator and likely causing an additional memory deference when using it to access the underlying pair / value.
What if you want to remove the item? What if you want to extract or merge it? What if you want to use it as a hint to emplace?
I find myself in a situation where I have to access a std::map by position. Since std::advance(iter, position) is slow af, I want to add a second data structure to speedup this operation.
My idea: Maintain a vector for every key-value pair in the map. Then access the vector by position, vector[position]->second.
When erasing/inserting a new element I obviously have to remove the iterator from the vector. But besides that the iterator-preserving properties of std::map seem to be sufficient.
Question: Is this a good idea?
Alternative: Maintain a vector of map::keys. Then access vector by position an use the key to lookup the value in the map,map[vector[position]]. Is this smarter?
If iteration through the map is your primary performance issue, then you should be using a flat_map (not as of yet part of the standard, but Boost has a decent one). If you don't have one of those, just use a vector<pair> that you keep sorted using the same comparison function you would have used in your map.
You can use std::lower_bound as the equivalent function for being able to find a value by its key. You can also use the iterator returned from std::lower_bound as the position for doing a single-element insertion of a new element. Everything else works just like any other vector; simply keep it sorted and you'll be fine.
std::map search, removal, and insertion operations have logarithmic complexity. The same complexity can be achieved by a sorted std::vector
Don't use a map, but a vector. Keep it sorted by key. Binary search by key is logaritgmic. Access by position is the fastest.
The only drawback is that inserting and removing needs memory reallocation. You may test its performance and consider if it's worth.
I am aware of the .find() method to check whether or not an element exists in a map. But suppose i have a map -- map -- with about 2000 elements and I want to add an element to the list. I first have to check if the element exists. Isn't it a bit inefficient to use .find() because the iterator has to 'iterate' over element in the map? Is there a more efficient and less time consuming way to check whether or not an element exists in a map?
std::map has logarithmic look-up complexity, so you won't have to visit all the elements to determine whether an element is in the map.
On the other hand, you can make the look-up and insert operation more concise by using std::map::emplace or std::map::insert:
auto [iter, ok] = the_map.emplace(the_key, a_value);
if (ok) {
// element was not in map so got inserted
}
This assumes that a_value either already exists, or that it is not an issue to construct it even if it does not end up being inserted into the map.
If you really need to use 'find and insert' idiom (and that could be because you can't construct a value object for the same key twice under any circumstances) and you are concerned about twice logarithmic complexity, you'd have to use std::map::equal_range.
The way to do this would be first use equal_range with your key, and than use one of returned iterators as a hint to insert.
I'd like to use the find() method, to catch a particular element of my std::map()
Now the return value is either the element I was looking for, or it it points to the end of the map if the element hasn't been found (C.f.: http://www.cplusplus.com/reference/map/map/find/ ).
So I'm using these two information whether to decide if an element is in the map or not. But what happens if the element is already inside but at the very end ? My if-query would then decide that it's not existing and therefor my program will be made up.
Am I getting this routine right, and if, how to prevent it ?
Thanks for your support
The last element in any container is not the same as the containers end iterator. The end iterator is actually one step beyond the last element in the container.
That means you can rest easily, and use find without worries. If the element you look for is the last element, you will get what you search for.
You can actually use if(!map.count(elem)) to check whether elem is present in the map, count() returns 0 if elem is not present in the map while find() returns an iterator which has to been compared to the end iterator, i.e., if(map.find(elem) != map.end()).
multimap offers the methods lower_bound and upper_bound. Both may return an iterator to a value with key greater than the desired, with lower_bound possibly yielding exactly the desired.
Now I want an iterator to a value where the key is strictly less the requested. If it were a map rather than multimap, this would be relatively simple to achieve as described here:
Returning the greatest key strictly less than the given key in a C++ Map.
But in a multimap, decrementing an iterator is not guaranteed to make it point to a strictly smaller key. So I would need to decrement repeatedly, until a smaller key is found. Not particularly nice.
Is there a more elegant way of doing this?
The keys will in general be floating-point.
My apologies, it turns out that you can actually do it with a single decrement. I just placed it wrong in my program, that was the real error.
AFAIK, lower/upper_bound will return iterator to FIRST element of this value, So you can decrease it
lower_bound points to the smallest element greater than or equal to the argument (or end). Thus decrementing it once gives you the desired element (if it exists).