How to avoid duplicate pairs / find a pair in multimap? - c++

I have some (working) code that uses a multimap<string,string>. I'd like to change it to disallow duplicate values on the same key (obviously different values on the same key are fine, otherwise I wouldn't use a multimap).
Surprisingly the type doesn't seem to have a built-in way to avoid duplicates nor to find a key-value pair (only to find a key). But I figure someone on SO must have a ready-made workaround. Anyone?

std::map<std::string, std::set<std::string>> would appear to have exactamondo the properties you are looking for (although inferior complexity to unordered_map and unordered_set).

Here's what I came up with:
template<class K, class V>
typename multimap<K, V>::const_iterator find_pair(const multimap<K, V>& map, const pair<K, V>& pair)
{
typedef multimap<K, V>::const_iterator it;
std::pair<it,it> range = map.equal_range(pair.first);
for (it p = range.first; p != range.second; ++p)
if (p->second == pair.second)
return p;
return map.end();
}
template<class K, class V>
bool insert_if_not_present(multimap<K, V>& map, const pair<K, V>& pair)
{
if (find_pair(map, pair) == map.end()) {
map.insert(pair);
return true;
}
return false;
}
(This is not efficient when there are a large numbers of values attached to a single key, but in my case there are very few values on each key.)

It seems that
std::set<std::pair<std::string,std::string>>>
will have exactly the properties you are looking for.
However it is not a map nor multimap. You can keep both multimap and set of key,value pairs or create this set for checking consistency only.
I would use this set and create an adapter over it to multimap interface. Maybe it is not the easiest solution to implement, but with best performance efficiency.
See "adapter design pattern" questions for references.
[UPDATE]
See my working example as a start point.
E.g. how to iterate over all values for the key - see:
typedef std::set<std::pair<std::string, std::string> > ssset;
ssset::iterator get_key(ssset& s, std::string key)
{
ssset::iterator it = s.lower_bound(std::make_pair(key, ""));
if (it != s.end() && it->first == key) return it;
return s.end();
}
for (ssset::iterator it = get_key(s, "abc"); it != s.end() && it->first == "abc"; ++it)
std::cout << it->first << "->" << it->second << std::endl;

My suggestion to you would be to wrap your multimap in a class and simply do the verification in the method where you add something into the map. The rest of the functions would simply pass through to the multimap's methods. It makes a lot of boiler plate code but if you ever need to do other types of verifications it'll be easier this way.

Related

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.'

Elegant and efficient algorithm for increasing values of a "vector<pair>"

I need to find an element in a vector<pair<int, float>> and increase the second value.
I tried an approach.
template <typename K, typename V>
struct match_first {
const K _k; match_first(const K& k) : _k(k) {}
bool operator()(const pair<K, V>& el) const {
return _k == el.first;
}
};
Eg to use.:
vector< pair<int, float> > vec;
vec.push_back(make_pair(2, 3.0));
vec.push_back(make_pair(3, 5.0));
vec.push_back(make_pair(1, 1.0));
vector< pair<int, float> >::iterator it = find_if(vec.begin(), vec.end(), match_first<int, float>(3));
if (it != vec.end()) {
it->second += 9;
}
There is a more efficient way of accomplishing this task?
A map seems more natural:
#include <map>
int main()
{
std::map<int, float> m;
m.insert(std::make_pair(2, 3.0));
m.insert(std::make_pair(3, 5.0));
m.insert(std::make_pair(1, 1.0));
auto it = m.find(3);
if (it != m.end()) {
it->second += 9;
}
}
It will also be faster because lookup is O(log(n))
You can reach the same complexity with a vector of sorted pairs by using std::lower_bound (or std::equal_range if keys can be repeated)
It depends on your constrains. If you have the unique key (the first element) you can use std::map<K,V> to hold your objects. Then increasing it is easy. If V has a default constructor initializing it to zero, you can even skip adding new elements and just increment (I am not sure it will work with ints through).
std::map<K,V> data;
data[key] = data[key] + 1;
the [] operator used for non-existent key will create the object for you using its default constructor. To just access data use at or find methods.
extending sehe's answer: You can use std::multimap in the same way if you may have duplicate keys. This container also keeps the <K,V> pair in sorted order(keys) so binary search approach obviously speed up things.
There is no exact answer to your question: it depends.
My first answer is: use std::find_if (available in <algorithm>, part of the C++ Standard Library), then profile your code. If the search turns out to be a bottleneck worthy of concern, then try another approach.
Beware of using a std::map, as it will sort the pairs by their first component (that is, the insertion order will be lost). In addition, it will not allow you to store two pairs with the same first component.
As others have mentioned, you can work around this caveats (if they are indeed caveats to your problem), but, like I mentioned before, it would only be worth your while if you demonstrate first that the search turned out to be a bottleneck after using the standard algorithms.

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.

Accessing std::map keys and values

How do you access an std::vector of the keys or values of an std::map?
Thanks.
Edit: I would like to access the actual elements, not just copies of their contents. essentially I want a reference, not a copy.
This is essentially what I am wanting to do:
std::map<std::string, GLuint> textures_map;
// fill map
glGenTextures( textures_map.size(), &textures_map.access_values_somehow[0] );
You can't do that because neither the values nor the keys are laid out consecutively in memory. Each key/value pair is allocated independently in memory. In a situation like yours, you have to copy the values around.
There isn't a single function you can call to get all of the keys or values from an STL map. Instead, you can use the map iterators to access this information:
for (map<K, V>::iterator itr = myMap.begin(); itr != myMap.end(); ++itr) {
// Access key as itr->first
// Access value as itr->second
}
If you want to write a function that takes these values and wraps them up in an STL vector, then you could do so like this:
template <typename K, typename V>
std::vector<K> GetKeys(const std::map<K, V>& m) {
std::vector<K> result;
result.reserve(m.size()); // For efficiency
for (typename std::map<K, V>::const_iterator itr = m.begin(); itr != m.end(); ++itr)
result.push_back(itr->first);
return result;
}
template <typename K, typename V>
std::vector<V> GetValues(const std::map<K, V>& m) {
std::vector<V> result;
result.reserve(m.size()); // For efficiency
for (typename std::map<K, V>::const_iterator itr = m.begin(); itr != m.end(); ++itr)
result.push_back(itr->second);
return result;
}
Note that in these template functions, the type of the iterator is
typename std::map<K, V>::const_iterator
instead of
std::map<K, V>::const_iterator
This is because const_iterator here is a dependent type - a type that depends on a template argument - and consequently for silly historical reasons must be prefaced by the typename keyword. There's a good explanation of this here.
Hope this helps!
As others have said in their answers, you can't access all the keys or values in the form of a std::vector without copying them - std::map isn't designed for this form of access.
For your situation, I would set up 2 containers:
std::vector<GLuint> texture_id;
std::map<std::string, size_t> texture_map;
where the vector stores the ID, and the map's value is the index to the ID in the vector. When you add a new texture, add the ID into the vector with push_back(), whose index is stored in a map entry with the last element's index, e.g.:
pair<map<string, size_t>::iterator, bool> result =
texture_map.insert(make_pair(new_texture_name, -1)); //ignore the index for now
if(result.second) { //true when the texture name isn't already in the map
texture_id.push_back(new_id);
result.first->second = texture_id.size()-1; //update the index in the map to this ID's element in texture_id
}
The push_back would maintain the same indexes for old IDs. Encapsulate all this within a class with functions to add and search for textures, just like in the answer to your other question.
This would let you call, after the IDs are loaded:
glGenTextures( textures_id.size(), &(textures_id[0]) );
... since std::vector guarantees that elements are consecutive to one another in memory.
edit: changed the map's value type; this was previously GLuint*, pointing at the vector's elements. Thanks to Oli Charlesworth for pointing out this design's flaw.
edit: added sample code
Further to what others have said, it sounds very much like you really want to put the values into a std::vector and then sort the vector; that way you can access the contents like you want to (i.e. via a pointer to a contiguous array of them).
Note that this assumes that reading will be your bottleneck, and that writing (i.e. setting up the vector) will be done relatively infrequently. If not, then you're probably better off sticking with the std::map, and then copying the contents into a temporary array/vector every time you want to use them in this fashion.

Checking value exist in a std::map - C++

I know find method finds the supplied key in std::map and return an iterator to the element. Is there anyway to find the value and get an iterator to the element? What I need to do is to check specified value exist in std::map. I have done this by looping all items in the map and comparing. But I wanted to know is there any better approach for this.
Here is what I have wrote
bool ContainsValue(Type_ value)
{
bool found = false;
Map_::iterator it = internalMap.begin(); // internalMap is std::map
while(it != internalMap.end())
{
found = (it->second == value);
if(found)
break;
++it;
}
return found;
}
Edit
How about using another map internally which stores value,key combination. So I can call find on it? Is find() in std::map doing sequential search?
Thanks
You can use boost::multi_index to create a bidirectional map - you can use either value of the pair as a key to do a quick lookup.
If you have access to the excellent boost library then you should be using boost::multi_index to create bidirectional map as Mark says. Unlike a std::map this allows you to look up by either the key or the value.
If you only have the STL to hand the following code will do the trick (templated to work with any kind of map where the mapped_type supports operator==):
#include <map>
#include <string>
#include <algorithm>
#include <iostream>
#include <cassert>
template<class T>
struct map_data_compare : public std::binary_function<typename T::value_type,
typename T::mapped_type,
bool>
{
public:
bool operator() (typename T::value_type &pair,
typename T::mapped_type i) const
{
return pair.second == i;
}
};
int main()
{
typedef std::map<std::string, int> mapType;
mapType map;
map["a"] = 1;
map["b"] = 2;
map["c"] = 3;
map["d"] = 4;
map["e"] = 5;
const int value = 3;
std::map<std::string, int>::iterator it = std::find_if( map.begin(), map.end(), std::bind2nd(map_data_compare<mapType>(), value) );
if ( it != map.end() )
{
assert( value == it->second);
std::cout << "Found index:" << it->first << " for value:" << it->second << std::endl;
}
else
{
std::cout << "Did not find index for value:" << value << std::endl;
}
}
How about using another map internally which stores value,key combination. So I can call find on it?
Yes: maintain two maps, with one map using one type of key and the other using the other.
Is find() in std::map doing sequential search?
No it's a binary search of a sorted tree: its speed is O(log(n)).
Look into boost's bidirectional maps: http://www.boost.org/doc/libs/1_38_0/libs/bimap/doc/html/index.html
It lets both values act like a key.
Otherwise, iteration is the way to go.
What you are requesting is precisely what std::find does (not the member function)
template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value );
No, you have to loop over the std::map and check all values manually. Depending on what you want to do, you could wrap the std::map in a simple class that also caches all of the values that are inserted into the map in something that's easily search-able and doesn't allow duplicates, like a std::set. Don't inherit from the std::map (it doesn't have a virtual destructor!), but wrap it so that you can do something like this:
WrappedMap my_map< std::string, double >;
my_map[ "key" ] = 99.0;
std::set< double > values = my_map.values(); // should give back a set with only 99.0 in it
An alternative to rolling your own would be to use the Boost bidirectional map, which is easily found in the posts below or by Google.
It really depends on what you want to do, how often you want to do it, and how hard it is to roll your own little wrapper class versus installing and using Boost. I love Boost, so that's a good way to go - but there's something nice and complete about making your own wrapper class. You have the advantage of understanding directly the complexity of operations, and you may not need the full reverse mapping of values => keys that's provided by the Boost bidirectional map.
Not a very best option but might be useful in few cases where user is assigning default value like 0 or NULL at initialization.
Ex.
< int , string >
< string , int >
< string , string >
consider < string , string >
mymap["1st"]="first";
mymap["second"]="";
for (std::map<string,string>::iterator it=mymap.begin(); it!=mymap.end(); ++it)
{
if ( it->second =="" )
continue;
}
I am adding this answer, if someone come here and looks for c++11 and above..
//DECLARE A MAP
std::map<int, int> testmap;
//SAMPLE DATA
testmap.insert(std::make_pair(1, 10));
testmap.insert(std::make_pair(2, 20));
testmap.insert(std::make_pair(3, 30));
testmap.insert(std::make_pair(4, 20));
//ELEMENTS WITH VALUE TO BE FOUND
int value = 20;
//RESULTS
std::map<int, int> valuesMatching;
//ONE STEP TO FIND ALL MATCHING MAP ELEMENTS
std::copy_if(testmap.begin(), testmap.end(), std::inserter(valuesMatching, valuesMatching.end()), [value](const auto& v) {return v.second == value; });
Possible that I don't fully understand what you're trying to accomplish. But to simply test whether or not a map contains a value, I believe you can use the std::map's built in find.
bool ContainsValue(Type_ value)
{
return (internalMap.find(value) != internalMap.end());
}