How do I compare the std::map's body members? - c++

I am keeping a structure B based on a key A in a stl::map. I am writing code that based on any updates to any member of the body of the above map from the old values, I will print an alert.
I have no idea hoew to do that. I have researched the internet. Any help would be appreciated. Thanks.

If the types used as key and values have operator== defined, the simplest expensive solution is keeping a backup of the map, and then comparing both maps:
std::map<key,value> the_map;
std::map<key,value> test_copy; // hidden from the rest of the code
// copied from the_map on particular instants
bool map_has_changed() {
return the_map != test_copy;
}

struct foo
{
// foo members
};
bool isEqual(const std::map<int,foo>& map1, const std::map<int,foo>& map2)
{
std::map<int,foo>::const_iterator itr1, itr2;
for (itr1 = map1.begin(), itr2 = map2.begin(); itr1 != map1.end(), itr2 != map2.end(); itr1++, itr2++)
{
if (itr1->first != itr2->first) return false;
if (!isEqual(itr1->second, itr2->second)) return false;
}
return true;
}
bool isEqual(const foo& f1, const foo& f2)
{
// some implementation that checks if f1 and f2 are equal
}
The downside of this implementation is that it assumes that the members of each map are in the same order (which means they are inserted into in the same order). If they could be a different order, then you will need to do something like this for the std::map isEqual:
bool isEqual(const std::map<int,foo>& map1, const std::map<int,foo>& map2)
{
std::map<int,foo>::const_iterator itr, temp;
for (itr = map1.begin(); itr != map1.end(); itr++)
{
temp = map2.find(itr->first);
if (!temp) return false;
if (!isEqual(itr->second, temp->second)) return false;
}
return true;
}
The first implementation will be faster, but again, it assumes that the map is in the same order.

You should iterate the map and do a comparison:
std::map<int, std::string> my_map;
int someint = 2;
std::string mystr = "tony";
std::map<int, std::string>::iterator it;
for (it = my_map.begin(); it != my_map.end() it++)
{
if (it->first == someint)
{
//do something
}
if (it->second == mystr)
{
// do something else
}
}
Make sure if your map contains custom objects that comparison operators are properly implemented.

Related

Is searching first with std::map::find then using [], to retrieve the value, optimised usually?

I find often this pattern in some codebase I work with:
std::map<std::string, std::string> mymap;
...
if (mymap.find(key) != mymap.end())
{
return mymap[key];
}
I know this can be replaced by
std::map<std::string, std::string> mymap;
...
auto it = mymap.find(key)
if (it != mymap.end())
{
return it->second;
}
But I was wondering if, in practice, the first version would get optimised to not search twice anyway?
No it will not be optimized. The pattern is too high level.
You could preferentially just do this instead:
std::map<std::string, std::string> mymap;
...
auto it = mymap.find(key);
if ( it != mymap.end())
{
return it->second;
}
You can check on the benchmark below:
int findOptim( int key )
{
auto it = mymap.find(key);
if (it != mymap.end())
{
return it->second;
}
return -1;
}
int findTrivial( int key ) {
if ( mymap.find(key) != mymap.end() ) {
return mymap[key];
}
return -1;
}
Benchmark: https://quick-bench.com/q/7mFWe8jlXD7J9GTDMNSiZdcGCgY

Getting min_element of from a vector of structs based on two variables

So I have a vector of structs that is defined and used in the following manner:
enum ID {
alpha,
beta,
gamma
};
using TimePoint = std::chrono::time_point<std::chrono::system_clock>;
typedef struct pInfo {
int bar;
int key;
ID id;
TimePoint tPoint;
} pInfo;
std::vector<pInfo> pMembers;
I would like to basically iterate through this vector after populating it with all the items and get a reference to the matching element that meets my criteria.
The criteria is that I would have a function that passes in a type of ID, and it will return a reference to the vector element that is the best fit which means the one with the lowest TimePoint.
So for reference the function would be something like: pInfo& getNext(ID p_id);
And if I have a vector that has a few elements of each, like 4 alpha, 4 gamma, 4 beta I want the function to only check the ones with ID == alpha if that's what I pass in.
Right now I was using something like this:
std::min_element(std::begin(pMembers), std::end(pMembers), [](auto&& lhs, auto&& rhs){return lhs.tPoint < rhs.tPoint};
But this doesn't account for me wanting to only get certain types.
How would I do something like this?
I would just store the objects with different IDs in different vectors, one vector for each ID:
std::map<ID, std::vector<pInfo>> pMembers;
If you can't or won't do that, then I'd use a filtering iterator adaptor. The following example uses Boost.Iterator:
auto const filter = [p_id](auto const& id) { return id == p_id; };
auto const compare = [](auto const& a, auto const& b) { return a.tPoint < b.tPoint; };
auto const it = std::min_element(boost::make_filter_iterator(filter, begin(pMembers), end(pMembers)),
boost::make_filter_iterator(filter, end(pMembers), end(pMembers)),
compare).base();
Expanding on Remy's answer, this is how I would write their first way of doing it:
auto const it = std::min_element(begin(pMembers), end(pMembers), [=](auto const& a, auto const& b)
{
return std::forward_as_tuple(a.id != p_id, a.tPoint)
< std::forward_as_tuple(b.id != p_id, b.tPoint);
});
Make the lambda capture the passed ID so it can be used in the comparisons, eg:
pInfo& getNext(ID p_id)
{
if (pMembers.empty())
throw ...; // nothing to search, can't return a reference to nothing, so throw an exception instead...
auto iter = std::min_element(std::begin(pMembers), std::end(pMembers),
[=](const pInfo &lhs, const pInfo &rhs)
{
if (lhs.id == p_id) {
if (rhs.id != p_id) return true;
}
else if (rhs.id == p_id) {
if (lhs.id != p_id) return false;
}
else {
return false;
}
return lhs.tPoint < rhs.tPoint;
}
);
if (iter->id != p_id)
throw ...; // p_id not found, can't return a reference to nothing, so throw an exception instead...
return *iter;
}
Alternatively, try something more like this:
pInfo& getNext(ID p_id)
{
std::vector<std::reference_wrapper<pInfo>> v;
std::copy_if(std::begin(pMembers), std::end(pMembers), std::back_inserter(v),
[=](const pInfo &item){ return item.id == p_id; }
);
if (v.empty())
throw ...; // p_id not found, can't return a reference to nothing, so throw an exception instead...
auto iter = std::min_element(std::begin(v), std::end(v),
[](const pInfo &lhs, const pInfo &rhs){ return lhs.tPoint < rhs.tPoint; }
);
return *iter;
}
You can also simply apply the range-based-for to this problem as follows:
DEMO
const pInfo& getNext(const std::vector<pInfo>& pMembers, ID p_id)
{
const pInfo* p{nullptr};
TimePoint min{TimePoint::max()};
for(const auto& p_i : pMembers)
{
if(p_i.id == p_id && p_i.tPoint < min)
{
min = p_i.tPoint;
p = &p_i;
}
}
if(!p){
throw std::runtime_error("no data.");
}
return *p;
}

conversion of iterator in for loop

error picture
The compiler says there is something wrong with the conversion of iterators in my for loops. Can someone tell me what's wrong exactly?
For this project, I have supposed to store values into keys. I am storing the keys and values in each node and each time theres a new key, there will be a new node with its vectors of values.
struct node{
int key;
vector<int> values;
};
class key_value_sequences {
public:
key_value_sequences() { }
~key_value_sequences() { }
key_value_sequences(const key_value_sequences& A) {
myList = A.myList;
v = A.v;
}
key_value_sequences& operator=(const key_value_sequences& A) {
if (this == &A) return *this;
myList = A.myList;
v = A.v;
return *this;
}
// YOU SHOULD USE C++ CONTAINERS TO AVOID RAW POINTERS
// IF YOU DECIDE TO USE POINTERS, MAKE SURE THAT YOU MANAGE MEMORY PROPERLY
// IMPLEMENT ME: SHOULD RETURN SIZE OF A SEQUENCE FOR GIVEN KEY
// IF NO SEQUENCE EXISTS FOR A GIVEN KEY RETURN -1
int size(int key) const {
if (find(v.begin(), v.end(), key)!=v.end()) {
for(list<node>::iterator it = myList.begin(); it != myList.end(); it++) {
if (it->key == key) {
return it->values.size();
}
}
}
else return -1;
}
// IMPLEMENT ME: SHOULD RETURN POINTER TO A SEQUENCE FOR GIVEN KEY
// IF NO SEQUENCE EXISTS FOR A GIVEN KEY RETURN nullptr
const int* data(int key) const {
if (find(v.begin(), v.end(), key)!=v.end()) {
for(list<node>::iterator it = myList.begin(); it != myList.end(); it++) {
if (it->key == key) {
return it->values.data();
break;
}
}
}
else return nullptr;
}
// IMPLEMENT ME: INSERT VALUE INTO A SEQUENCE IDENTIFIED BY GIVEN KEY
void insert(int key, int value) {
if(v.size() == 0) { //empty list
v.push_back(key);
node n;
n.key = key;
n.values.push_back(value);
myList.push_back(n);
}
else if((find(v.begin(), v.end(), key)!=v.end())) { //if key exists already
for(list<node>::iterator it = myList.begin(); it != myList.end(); it++) {
if (it->key == key) {
it->values.push_back(value);
break;
}
}
}
else { //if theres no existing key
v.push_back(key);
node n;
n.key = key;
n.values.push_back(value);
myList.push_back(n);
}
}
private:
vector<int> v;
list<node> myList;
}; // class key_value_sequences
Your size and data methods are declared const. Therefore myList is const and its begin method returns const_iterators. Change list::iterator into list::const_iterator and it should work fine.

how to easily check if a std::map and std::unordered_map contains the same elements

I am writing a Google Test unit test, and I want to check if the content of an unordered_map<std::string, std::string> is the same as an std::map<std::string, std::string>
I don't think std::equal will work, as elements in the std::map are sorted according a criterion. The order is not important.
I don't think there is nicer way than just going over all elements of one map and checking if they are present in the other map. If you also check that the amount of elements is the same, you'll know whether the maps are completely the same.
For example:
template<typename K, typename E>
bool maps_equal(const std::map<K, E> &map, const std::unordered_map<K, E> &unordered_map) {
return
map.size() == unordered_map.size() &&
std::all_of(map.cbegin(), map.cend(), [&](const std::pair<const K, E> &item) {
auto iter = unordered_map.find(item.first);
return iter != unordered_map.end() && iter->second == item.second;
});
}
You can create an unordered_map with a map, and then compare two unordered_map. And vice versa.
std::unordered_map<std::string, std::string> m1;
std::map<std::string, std::string> m2;
std::unordered_map<std::string, std::string> m3(m2.begin(), m2.end());
if (m1 == m3) {}
I will ask an obvious question, but it really changes everything:
Is the notion of equality in the map compatible with the notion of equality in the unordered_map?
As an example of incompatible definitions:
struct Point3D { std::int32_t x, y, z };
struct MapLess {
bool operator()(Point3D const& left, Point3D const& right) const {
return std::tie(left.x, left.y) < std::tie(right.x, right.y);
}
};
bool operator==(Point3D const& left, Point3D const& right) {
return std::tie( left.x, left.z)
== std::tie(right.x, right.z);
}
In this (contrived) case, we could have:
map: (1, 2, 3) and (1, 3, 3)
unordered_map: (1, 2, 3) and (1, 2, 4)
and a naive look-up would report that the map is included in the unordered_map which since they both have the same size would lead to the erroneous conclusion that they are equal.
The solution if a canonical notion of equality exists is to verify, after each look-up, that the look-up result is effectively the same as the original.
template <typename M1, typename M2>
bool equal(M1 const& left, M2 const& right) {
if (left.size() != right.size()) { return false; }
for (auto const& e: left) {
auto const it = right.find(e.first);
if (it == right.end()) { return false; }
if (it->first != e.first) { return false; }
if (it->second != e.second) { return false; }
}
return true;
}
Note: this could be rewritten with std::all and a single boolean expression; it's a matter of taste, I prefer breaking it down.
If no canonical notion of equality exists, then a reverse look-up can replace the equality check:
template <typename M1, typename M2>
bool equal(M1 const& left, M2 const& right) {
if (left.size() != right.size()) { return false; }
for (auto e = left.begin(), end = left.end(); e != end; ++e) {
auto const it = right.find(e->first);
if (it != right.end()) { return false; }
if (left.find(it->first) != e) { return false; }
if (it->second != e->second) { return false; }
}
return true;
}
This is of course slightly more expensive.

Intersection of two `std::map`s

Given that I have two std::maps, say:
map<int, double> A;
map<int, double> B;
I'd like to get the intersection of the two maps, something of the form:
map<int, pair<double,double> > C;
Where the keys are the values in both A and B and the value is a pair of the values from A and B respectively.
Is there a clean way using the standard-library?
#include <map>
#include <utility>
template <typename KeyType, typename LeftValue, typename RightValue>
std::map<KeyType, std::pair<LeftValue, RightValue>>
IntersectMaps(const std::map<KeyType, LeftValue>& left,
const std::map<KeyType, RightValue>& right) {
std::map<KeyType, std::pair<LeftValue, RightValue>> result;
typename std::map<KeyType, LeftValue>::const_iterator il = left.begin();
typename std::map<KeyType, RightValue>::const_iterator ir = right.begin();
while (il != left.end() && ir != right.end()) {
if (il->first < ir->first)
++il;
else if (ir->first < il->first)
++ir;
else {
result.insert(std::make_pair(il->first, std::make_pair(il->second, ir->second)));
++il;
++ir;
}
}
return result;
}
I haven't tested this, or even compiled it... but it should be O(n). Because it's templated it should work with any two maps that share the same key type.
I don't think there is a pure STL way of implementing what you want. Manual implementation should not be too complicated.
Note that std::set_intersection is not a solution. The main reason being that it compares the dereferenced iterators and then copies one of the elements to the output iterator.
While comparison of the full dereferenced iterator includes the associated value (which I understand you do not want to consider as part of the key), can be solved by providing a comparison functor that would only test the key (std::pair<const Key, Value>::first), the problem of the algorithm copying only one of the two values and not composing the solution cannot be tackled externally.
EDIT: A simple linear time implementation of the function:
Note, as #Mark Ransom comments, that this solution adds an extra requirement: the keys must be equality comparable. That is not an issue with his solution here, or similarly in the answer by #Matthiew M here. It would be shameful to modify this algorithm with that fix :)
Another great advantage of #Mark's implementation is that it can compose from maps that store different value types as long as the keys are the same (which seems like an obvious requirement). I wish I would upvote more than once there..
template <typename Key, typename Value>
std::map<Key,std::pair<Value,Value> >
merge_maps( std::map<Key,Value> const & lhs, std::map<Key,Value> const & rhs )
{
typedef typename std::map<Key,Value>::const_iterator input_iterator;
std::map<Key, std::pair<Value,Value> > result;
for ( input_iterator it1 = lhs.begin(), it2 = rhs.begin(),
end1 = lhs.end(), end2 = rhs.end();
it1 != end1 && it2 != end2; )
{
if ( it1->first == it2->first )
{
result[it1->first] = std::make_pair( it1->second, it2->second );
++it1; ++it2;
}
else
{
if ( it1->first < it2->first )
++it1;
else
++it2;
}
}
return result;
}
#include <map>
using namespace std;
typedef int KeyType;
typedef double ValueType;
typedef map< KeyType, ValueType > MyMap;
typedef MyMap::iterator MyMapIter;
typedef MyMap::const_iterator MyMapConstIter;
typedef pair< ValueType, ValueType > ValueTypePair;
typedef map< KeyType, ValueTypePair > MyMapIntersection;
int main() {
MyMap A;
MyMap B;
MyMapIntersection C;
// fill up A, B
for( MyMapConstIter cit = A.begin(); cit != A.end(); ++cit ) {
const KeyType x = cit->first;
MyMapConstIter found = B.find( x );
if( found != B.end() ) {
ValueTypePair valuePair =
ValueTypePair( cit->second, found->second );
C.insert( pair< KeyType, ValueTypePair>( x, valuePair ) );
}
}
}
A (better) solution based on the fact that the maps are sorted. (Shame on me that I overlooked it.) Thanks to David Rodríguez - dribeas for the suggestion.
#include <map>
using namespace std;
typedef int KeyType;
typedef double ValueType;
typedef map< KeyType, ValueType > MyMap;
typedef MyMap::iterator MyMapIter;
typedef MyMap::const_iterator MyMapConstIter;
typedef pair< ValueType, ValueType > ValueTypePair;
typedef map< KeyType, ValueTypePair > MyMapIntersection;
void constructInsert( MyMapIntersection & c, MyMapConstIter const & acit,
MyMapConstIter const & bcit ) {
ValueTypePair valuePair = ValueTypePair( acit->second, bcit->second );
c.insert( pair< KeyType, ValueTypePair>( acit->first, valuePair ) );
}
int main() {
MyMap A;
MyMap B;
MyMapIntersection C;
// fill up A, B
MyMapConstIter acit, bcit;
for( acit = A.begin(), bcit = B.begin();
(acit != A.end()) && (bcit != B.end()); /* Inside loop */ ) {
const KeyType aKey = acit->first;
const KeyType bKey = bcit->first;
if( aKey < bKey ) {
++acit;
}
else if( aKey == bKey ) {
constructInsert( C, acit, bcit );
++acit;
++bcit;
}
else {
++bcit;
}
}
}
Okay, let's get ready to get your hands dirty :)
I'll be using std::mismatch and std::transform
First of all, some types:
typedef std::map<int, double> input_map;
typedef input_map::const_reference const_reference;
typedef input_map::const_iterator const_iterator;
typedef std::pair<const_iterator,const_iterator> const_pair;
typedef std::map<int, std::pair<double,double> > result_map;
Then predicates
bool less(const_reference lhs, const_reference rhs)
{
return lhs.first < rhs.first;
}
result_map::value_type pack(const_reference lhs, const_reference rhs)
{
assert(lhs.first == rhs.first);
return std::make_pair(lhs.first, std::make_pair(lhs.second, rhs.second));
}
Now main:
result_map func(input_map const& m1, input_map const& m2)
{
if (m1.empty() || m2.empty()) { return result_map(); }
// mismatch unfortunately only checks one range
// god do I hate those algorithms sometimes...
if (*(--m1.end()) < *(--m2.end()) { return func(m2, m1); }
const_pair current = std::make_pair(m1.begin(), m2.begin()),
end = std::make_pair(m1.end(), m2.end());
result_map result;
// Infamous middle loop, the check is middle-way in the loop
while(true)
{
const_pair next = std::mismatch(p.first, end.first, p.second, less);
std::transform(current.first, next.first, current.second,
std::inserter(result, result.begin()), pack);
// If any of the iterators reached the end, then the loop will stop
if (next.first == end.first || next.second == end.second) { break; }
// Advance the lesser "next"
if (less(*next.first, *next.second)) { ++next.first; }
else { ++next.second; }
current = next;
}
return result;
}
I find this solution quite elegant... notwithstanding the awkard setup part since we need to ensure that the first range ends up quicker than the second because of mismatch...
Notice that the advance is really stupid, we could loop specifically here until we had *next.first.key == *next.second.key but it would complicate the loop.
I really don't find this better than a handcrafted loop though... consider:
result_map func2(input_map const& lhs, input_map const& rhs)
{
result_map result;
for (const_iterator lit = lhs.begin(), lend = lhs.end(),
rit = rhs.begin(), rend = rhs.end();
lit != lend && rit != rend;)
{
if (lit->first < rit->first) { ++lit; }
else if (rit->first < lit->first) { ++rit; }
else
{
result[lit->first] = std::make_pair(lit->second, rit->second);
++lit, ++rit;
}
}
return result;
}
It's much more compact, probably more efficient... sometimes the functions you're looking are not general enough to be in the STL :)
EDIT: Since I was pretty sure there was a better STL-like solution to this, I figured one out. It's different enough that I'm posting it as a separate answer.
There are a few tricks to this. Firstly, you'd like to use set_intersection, but you have two maps. The solution is a custom comparator and the std::transform algorithm. Someone more familiar with the standard library than me can probably optimize this, but it works. Note that boost::bind would allow you to cut down on the silly helper functions that make this work.
#include <algorithm>
#include <map>
#include <set>
bool myLess(const std::map<int,double>::value_type &v1,
const std::map<int,double>::value_type &v2) {
return v1.first < v2.first;
}
int getKey(const std::map<int,double>::value_type &v) {
return v.first;
}
struct functor {
std::map<int,double> &m1,&m2;
functor(std::map<int,double> &im1, std::map<int,double> &im2) : m1(im1), m2(im2) {}
std::pair<int,std::pair<double,double> > operator() (int x) {
return std::make_pair(x, std::make_pair(m1[x],m2[x]));
}
};
int main() {
std::map<int,double> m1, m2;
m1[0]=0;m1[1]=1; m1[2]=2; m1[3]=3;
m2[1]=11;m2[2]=12;m2[3]=13;m2[4]=14;
std::set<int> keys1,keys2,keys;
//Extract the keys from each map with a transform
std::transform(m1.begin(),m1.end(),std::inserter(keys1,keys1.begin()),getKey);
std::transform(m2.begin(),m2.end(),std::inserter(keys2,keys2.begin()),getKey);
//set_intersection to get the common keys
std::set_intersection(keys1.begin(),keys1.end(),keys2.begin(),keys2.end(),
std::inserter(keys,keys.begin()));
std::map<int, std::pair<double,double> > result;
functor f(m1,m2); //stash our maps into the functor for later use
//transform from the key list to the double-map
std::transform(keys.begin(),keys.end(),std::inserter(result,result.begin()),f);
return 0;
}
Like much of C++, the final use of everything is fairly slick (everything in main()), but the setup is more verbose than we would really like.
The following is a simplification of my previous answer, mostly taking advantage of the fact that set_intersection CAN be used with maps as input, but only if you make the output a set of std::pairs. The result also cuts down intermediates to a single "common keys" list.
#include <algorithm>
#include <map>
#include <set>
struct cK { //This function object does double duty, the two argument version is for
//the set_intersection, the one argument version is for the transform
std::map<int,double> &m1,&m2;
cK(std::map<int,double> &im1, std::map<int,double> &im2) : m1(im1), m2(im2)
std::pair<int,std::pair<double,double> > operator() (std::pair<int,double> v
return std::make_pair(v.first, std::make_pair(m1[v.first],m2[v.first]));
}
bool operator() (std::pair<int,double> v1, std::pair<int,double> v2) {
return v1.first < v2.first;
}
};
int main() {
std::map<int,double> m1, m2;
m1[0]=0;m1[1]=1; m1[2]=2; m1[3]=3;
m2[1]=11;m2[2]=12;m2[3]=13;m2[4]=14;
// Get the subset of map1 that has elements in map2
std::set<std::pair<int,double> > sIntersection;
cK compareKeys(m1,m2);
std::set_intersection(m1.begin(),m1.end(),m2.begin(),m2.end(),
std::inserter(sIntersection,sIntersection.begin()),compareKeys);
// Use a custom transform to produce an output set
std::map<int, std::pair<double,double> > result;
std::transform(sIntersection.begin(),sIntersection.end(),
std::inserter(result,result.begin()), compareKeys);
return 0;
}
Almost a year after... but nevertheless :)
This one is for a set container, but you can easily change it to use a map:
template <class InputIterator, class OutputIterator>
OutputIterator intersect(InputIterator lf, InputIterator ll,
InputIterator rf, InputIterator rl,
OutputIterator result)
{
while(lf != ll && rf != rl)
{
if(*lf < *rf)
++lf;
else if(*lf > *rf)
++rf;
else
{
*result = *lf;
++lf;
++rf;
}
}
return result;
}
Usage:
intersect(set1.begin(), set1.end(),
set2.begin(), set2.end(),
inserter(output_container, output_container.begin()));
set1 and set2 are both set containers whilst output_container can be set, list, array etc..
inserter generates an insert iterator
template<typename K, typename V>
std::map<K, V> UnionMaps(const std::map<K, V> & left, const std::map<K, V> & right)
{
std::map<K, V > result;
typename std::map<K, V>::const_iterator il = left.begin();
typename std::map<K, V>::const_iterator ir = right.begin();
while (il != left.end() && ir != right.end())
{
if ((il->first < ir->first)){
result.insert(make_pair(il->first, il->second));
++il;
}else if ((ir->first < il->first)){
result.insert(make_pair(ir->first, ir->second));
++ir;
}else{
result.insert(make_pair(il->first, il->second+ir->second));//add
++il;
++ir;
}
}
while (il != left.end() ){
result.insert(make_pair(il->first, il->second));
il++;
}
while (ir != right.end() ){
result.insert(make_pair(ir->first, ir->second));
ir++;
}
return result;
}