I have map, which keys are std::string. I want to find those elements in the map which starts with "DUPA/"
prefix. Finding lower bound is easy but upper bound is a bit problematic. I wrote such piece of code:
const char* prefix = "DUPA/";
const char* firstAfterPrefix = "DUPA0";
auto prefixedBeginIt = myMap.upper_bound(prefix);
auto prefixedEndIt = myMap.lower_bound(firstAfterPrefix);
The code works fine but I don't consider it to be elegant, as one has to know that 0
is first next to /
in ASCII table. Second way would be to copy prefix and increment last sign. Do you know more elegant solution?
To check for the existence of a particular key in the map, the standard solution is to use the public member function find() of the ordered or the unordered map container, which returns an iterator to the key-value pair if the specified key is found, or iterator to the end of the container if the specified key is not ...
Check if map contains a key using std::map::countsize_type count (const key_type& K) const; It finds & returns the count of number of elements in map with key K. As map contains elements with unique key only. So, it will return 1 if key exists else 0.
The match_results::prefix() is an inbuilt function in C++ which is used to get the string which is preceding the matched string in the input target string.
C++ map find() function. C++ map find() function is used to find an element with the given key value k. If it finds the element then it returns an iterator pointing to the element. Otherwise, it returns an iterator pointing to the end of the map, i.e., map::end().
If you can assume that CHAR_MAX
would not be a valid character in your strings, then you can create firstAfterPrefix
by appending CHAR_MAX
(or the maximum value of your character type if it's not char
).
std::string prefix = "DUPA/";
constexpr auto char_max = std::numeric_limits<decltype(prefix)::value_type>::max();
std::string firstAfterPrefix = prefix + char_max;
auto prefixedBeginIt = myMap.lower_bound(prefix);
auto prefixedEndIt = myMap.lower_bound(firstAfterPrefix);
Note the use of lower_bound
for both bounds. Like Gill I am using std::string
to simplify the exposition.
If you can use C++14 and specify the Compare
template argument of the container then another way is to use a custom probe object:
struct PrefixProbe { std::string_view prefix; };
bool operator<(PrefixProbe a, std::string_view b) { return a.prefix < b.substr(0, a.prefix.size()); }
bool operator<(std::string_view a, PrefixProbe b) { return a.substr(0, b.prefix.size()) < b.prefix; }
std::map<std::string, myValue, std::less<>> myMap;
// ^~~~~~~~~~~
// where the magic happens
auto prefixBegin = myMap.lower_bound(PrefixProbe { prefix });
auto prefixEnd = myMap.upper_bound(PrefixProbe { prefix });
std::string_view
is C++17 but is not required to make this work.
equal_range
would reduce the last two lines to a single line:
auto [ prefixBegin, prefixEnd ] = myMap.equal_range(PrefixProbe { prefix });
If you are prepared to use the STL algorithms instead of the container member functions then this can be done without altering the container type, but would be less efficient:
auto prefixBegin = std::lower_bound(cbegin(myMap), cend(myMap), PrefixProbe { prefix }, std::less<>{});
auto prefixEnd = std::upper_bound(cbegin(myMap), cend(myMap), PrefixProbe { prefix }, std::less<>{});
auto [ prefixBegin, prefixEnd ] = std::equal_range(cbegin(myMap), cend(myMap), PrefixProbe { prefix }, std::less<>{});
I think the solution you mentioned is already the most elegant. The KISS way loses a lot of performance, that is, checking the key each time:
while(prefixedBeginIt->first == prefix)
{
//...
++prefixedBeginIt;
}
Thus I think calculating the next char is the best approach:
std::string firstAfterPrefix = prefix;
++firstAfterPrefix[firstAfterPrefix.length() - 1];
auto prefixedEndIt = myMap.lower_bound(firstAfterPrefix);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With