What's the best way to compare two QMaps and remove the entries not in first map and add the entries of map two to the first one? Let's say I have a map with {1,2,3} and a map with {1,2,4} and I want to keep in first map {1,2,4}. Thanks.
Based on your example maps this:
void removeNonUniqueThenAppend(QMap<QString, QString>& map1, QMap<QString, QString> map2)
{
QMap<QString, QString>::iterator iterator = map1.begin();
while (iterator != map1.end()) {
if (!map2.contains(iterator.key())) {
iterator = map1.erase(iterator);
}
else {
map2.remove(iterator.key());
++iterator;
}
}
map1.unite(map2);
}
You may adapt the following to Qt:
template <typename K, typename V>
void my_merge(std::map<K, V>& m1, const std::map<K, V> m2)
{
auto it1 = m1.begin();
auto it2 = m2.begin();
while (it1 != m1.end() && it2 != m2.end()) {
if (*it1 < *it2) { // Only in m1
it1 = m1.erase(it1);
} else if (*it2 < *it1) { // Only in m2
m1.insert(it1, *it2); // with hint.
++it2;
} else { // In both
++it1;
++it2;
}
}
m1.erase(it1, m1.end());
m1.insert(it2, m2.end());
}
Demo
Demo with hint
Related
I would like to get vectors representing each possible subset of specific length of consecutive elements of a map, per the inherent ordering, for example:
How can this be done?
You may iterate iver the map as key are ordered:
std::vector<std::array<Sample, 3u>> get_triplets(const std::map<int, Sample>& samples)
{
if (samples.size() < 3) {
return {};
}
std::vector<std::array<Sample, 3u>> res;
auto it = samples.begin();
auto it1 = std::next(it);
auto it2 = std::next(it1);
for (; it2 != samples.end(); ++it, ++it1, ++it2) {
res.push_back({{it->second, it1->second, it2->second}});
}
return res;
}
Live Demo
Edit: to have n-uplets, small changes from previous triplet version:
std::vector<std::vector<Sample>> get_n_uplets(std::size_t n, const std::map<int, Sample>& samples)
{
if (samples.size() < n) {
return {};
}
std::vector<std::vector<Sample>> res;
auto first = samples.begin();
auto last = std::next(first, n - 1);
for (; last != samples.end(); ++first, ++last) {
std::vector<Sample> inner;
for (auto it = first; it != std::next(last); ++it) {
inner.push_back(it->second);
}
res.push_back(inner);
}
return res;
}
Live Demo
I have the following code using range-based for-loops (C++11):
vector<atom> protein;
...
for(atom &atom1 : protein) {
...
for(atom &atom2 : protein) {
if(&atom1 != &atom2) {
...
}
}
}
Is there a better/cleaner/faster way to write this nested loops? Isn't there a way to include the if condition in the second loop?
Similar to ronag's answer is a more generic version:
template<typename C, typename Op>
void each_unique_pair(C& container, Op fun)
{
for(auto it = container.begin(); it != container.end() - 1; ++it)
{
for(auto it2 = std::next(it); it2 != container.end(); ++it2)
{
fun(*it, *it2);
fun(*it2, *it);
}
}
}
UPDATE
template<typename C, typename O1, typename O2>
void each_value_and_pair(C& container, O1 val_fun, O2 pair_fun)
{
auto it = std::begin(container);
auto end = std::end(container);
if(it == end)
return;
for(; it != std::prev(end); ++it)
{
val_fun(*it);
for(auto it2 = std::next(it); it2 != end; ++it2)
{
pair_fun(*it2, *it);
pair_fun(*it, *it2);
}
}
}
Which is used like this:
main()
{
std::vector<char> values;
// populate values
// ....
each_value_and_pair(values,
[](char c1) { std::cout << "value: " << c1 << std::endl;},
[](char c1, char c2){std::cout << "pair: " << c1 << "-" << c2 << std::endl;});
}
Sad but true.
How about normal loops with iterators and auto keyword?
I think this might be what you are looking for:
for(auto it1 = std::begin(protein1); it1 != std::end(protein); ++it1)
{
for(auto it2 = std::next(it1); it2 != std::end(protein); ++it2)
{
auto& atom1 = *it1;
auto& atom2 = *it2;
// ...
}
}
you're method is just fine.
if you want to save the if statement you can
vector<atom> protein;
int i, j;
...
for(i = 0; i < protein.size() : i++) {
atom &atom1 = protein.at(i);
for(j = i+1; j < protein.size() ; j++) {
atom &atom2 = protein.at(j);
// Do something
// Swap the atom2 and atom1
// Do something again
}
}
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.
I've modified James' flattening iterator to act as a bidirectional iterator if possible, but I don't think my changes are very elegant (particularly relying on a bool to see if the inner iterator has been set). However, I can't seem to come up with a nicer solution. Does anyone have any ideas?
#include <algorithm>
#include <iostream>
#include <set>
#include <vector>
#include <iterator>
#include <type_traits>
// An iterator that "flattens" a container of containers. For example,
// a vector<vector<int>> containing { { 1, 2, 3 }, { 4, 5, 6 } } is iterated as
// a single range, { 1, 2, 3, 4, 5, 6 }.
template <typename OuterIterator>
class flattening_iterator
{
public:
typedef OuterIterator outer_iterator;
typedef typename std::iterator_traits<outer_iterator>::value_type::iterator inner_iterator;
typedef typename std::iterator_traits<outer_iterator>::iterator_category outer_category;
typedef typename std::iterator_traits<inner_iterator>::iterator_category inner_category;
typedef typename std::common_type<outer_category, inner_category>::type common_category;
typedef typename std::conditional<std::is_same<common_category, std::random_access_iterator_tag>::value,
std::bidirectional_iterator_tag,
common_category>::type iterator_category;
typedef typename std::iterator_traits<inner_iterator>::value_type value_type;
typedef typename std::iterator_traits<inner_iterator>::difference_type difference_type;
typedef typename std::iterator_traits<inner_iterator>::pointer pointer;
typedef typename std::iterator_traits<inner_iterator>::reference reference;
flattening_iterator() { }
flattening_iterator(outer_iterator it, outer_iterator begin, outer_iterator end)
: outer_it_(it),
outer_begin_(begin),
outer_end_(end),
inner_it_assigned_(false)
{
if (outer_begin_ == outer_end_) { return; }
if (outer_it_ == outer_end_) { return; }
inner_it_ = outer_it_->begin();
inner_it_assigned_ = true;
advance_past_empty_inner_containers();
}
reference operator*() const { return *inner_it_; }
pointer operator->() const { return &*inner_it_; }
flattening_iterator& operator++()
{
++inner_it_;
if (inner_it_ == outer_it_->end())
advance_past_empty_inner_containers();
return *this;
}
flattening_iterator operator++(int)
{
flattening_iterator it(*this);
++*this;
return it;
}
flattening_iterator& operator--()
{
if(!inner_it_assigned_)
{
if(outer_begin_ != outer_end_)
{
decrement_through_empty_inner_containers();
}
return *this;
}
if(inner_it_ == outer_it_->begin())
{
decrement_through_empty_inner_containers();
}
else
{
--inner_it_;
}
return *this;
}
flattening_iterator operator--(int)
{
flattening_iterator it(*this);
--*this;
return it;
}
friend bool operator==(const flattening_iterator& a,
const flattening_iterator& b)
{
if (a.outer_it_ != b.outer_it_)
return false;
if(a.outer_it_ != a.outer_end_ &&
b.outer_it_ != b.outer_end_ &&
a.inner_it_assigned_ == false &&
b.inner_it_assigned_ == false)
return true;
if (a.outer_it_ != a.outer_end_ &&
b.outer_it_ != b.outer_end_ &&
a.inner_it_ != b.inner_it_)
return false;
return true;
}
friend bool operator!=(const flattening_iterator& a,
const flattening_iterator& b)
{
return !(a == b);
}
private:
void advance_past_empty_inner_containers()
{
while (outer_it_ != outer_end_ && inner_it_ == outer_it_->end())
{
++outer_it_;
if (outer_it_ != outer_end_)
inner_it_ = outer_it_->begin();
}
}
void decrement_through_empty_inner_containers()
{
--outer_it_;
while(outer_it_ != outer_begin_ && outer_it_->begin() == outer_it_->end())
{
--outer_it_;
}
if(outer_it_->begin() != outer_it_->end())
{
inner_it_ = --outer_it_->end();
inner_it_assigned_ = true;
}
}
outer_iterator outer_it_;
outer_iterator outer_begin_;
outer_iterator outer_end_;
inner_iterator inner_it_;
bool inner_it_assigned_;
};
template <typename Iterator>
flattening_iterator<Iterator> flatten(Iterator start, Iterator first, Iterator last)
{
return flattening_iterator<Iterator>(start, first, last);
}
template <typename Iterator>
std::reverse_iterator<flattening_iterator<Iterator>> flatten_reverse(Iterator start, Iterator first, Iterator last)
{
return std::reverse_iterator<flattening_iterator<Iterator>>(flatten(start, first, last));
}
int main()
{
std::vector<std::vector<int>> v(3);
int i(0);
for (auto it(v.begin()); it != v.end(); ++it)
{
it->push_back(i++); it->push_back(i++);
it->push_back(i++); it->push_back(i++);
}
v.insert(v.begin(), std::vector<int>());
v.insert(v.begin(), std::vector<int>());
v.insert(v.begin() + 4, std::vector<int>());
v.push_back(std::vector<int>());
v.push_back(std::vector<int>());
for (auto it(flatten(v.begin(), v.begin(), v.end())), end = flatten(v.end(), v.begin(), v.end());
it != end;
++it)
{
std::cout << *it << ", ";
}
std::cout << "\n";
for (auto it(flatten_reverse(v.end(), v.begin(), v.end())), end = flatten_reverse(v.begin(), v.begin(), v.end());
it != end;
++it)
{
std::cout << *it << ", ";
}
std::cout << "\n";
std::vector<std::vector<int>> v2;
for (auto it(flatten(v2.end(), v2.begin(), v2.end())), end = flatten(v2.begin(), v2.begin(), v2.end());
it != end;
--it)
{
std::cout << *it << ", ";
}
std::cout << "\n";
}
Great question, and great attempt.
An iterator should always refer to a valid value, or one-past-the-end. *iter should always be valid unless iter == end where end is one-past-the-end. That "one-past-the-end" iterator is the cause of your worries. Either inner_it_ refers to a valid value, or your iterator is one-past-the-end.
James's "one-past-the-end" iterator exists when outer_it_ == outer_end_, and this is the situation you need to check for. This is the only situation in which inner_it_ should have an invalid value. As a result, you can get rid of the bool and just directly check outer_it_ == outer_end_.
Also, I find this line suspect:
inner_it_ = --outer_it_->end();
Is it possible that outer_it_ is a typedef to a pointer? If so, you can't call -- on a pointer value. This will definitely work:
inner_it_ = outer_it_->end();
--inner_it_;
and moreover, it conveys intent better, because the first looks like you're decrementing the end() iterator itself!
Allegedly you cannot just erase/remove an element in a container while iterating as iterator becomes invalid. What are the (safe) ways to remove the elements that meet a certain condition? please only stl, no boost or tr1.
EDIT
Is there a more elegant way if I want to erase a number of elements that meet a certain criteria, perhaps with using functor and for_each or erase algorithm ?
You can as long as you don't invalidate your iterator after you've erased it:
MyContainer::iterator it = myContainer.begin();
while(it != myContainer.end())
{
if (*it == matchingValue)
{
myContainer.erase(it++);
}
else
{
++it;
}
}
Example with std::vector
#include <vector>
using namespace std;
int main()
{
typedef vector <int> int_vector;
int_vector v(10);
// Fill as: 0,1,2,0,1,2 etc
for (size_t i = 0; i < v.size(); ++i){
v[i] = i % 3;
}
// Remove every element where value == 1
for (int_vector::iterator it = v.begin(); it != v.end(); /* BLANK */){
if (*it == 1){
it = v.erase(it);
} else {
++it;
}
}
}
bool IsOdd( int i )
{
return (i&1)!=0;
}
int a[] = {1,2,3,4,5};
vector<int> v( a, a + 5 );
v.erase( remove_if( v.begin(), v.end(), bind1st( equal_to<int>(), 4 ) ), v.end() );
// v contains {1,2,3,5}
v.erase( remove_if( v.begin(), v.end(), IsOdd ), v.end() );
// v contains {2}
Viktor's solution has the upside of being able to do something with the element before removing. (I wasn't able to do this with remove_if or remove_copy_if.) But I prefer to use std::find_if so I never have to increment the iterator myself:
typedef vector<int> int_vector;
int_vector v;
int_vector::iterator itr = v.begin();
for(;;)
{
itr = std::find_if(itr, v.end(), Predicate(4));
if (itr == v.end())
{
break;
}
// do stuff with *itr here
itr = v.erase(itr); // grab a new, valid iterator
}
Where Predicate could be bind1st( equal_to<int>(), 4 ) or something like this:
struct Predicate : public unary_function<int, bool>
{
int mExpected;
Predicate(int desired) : mExpected(desired) {}
bool operator() (int input)
{
return ( input == mExpected );
}
};
I prefer version with while:
typedef std::list<some_class_t> list_t;
void f( void ) {
// Remove items from list
list_t::iterator it = sample_list.begin();
while ( it != sample_list.end() ) {
if ( it->condition == true ) {
it = sample_list.erase( it );
} else ++it;
}
}
With while there is no danger to increment it twice as it could be in for loop.
1.For std::vector<> :
std::vector <int> vec;
vec.erase(std::remove(vec.begin(),vec.end(), elem_to_remove), vec.end());
2.For std::map<> always use std::map::erase()
std::map<int,std::string> myMap;
myMap.emplace(std::make_pair(1, "Hello"));
myMap.emplace(std::make_pair(2, "Hi"));
myMap.emplace(std::make_pair(3, "How"));
myMap.erase( 1);//Erase with key
myMap.erase(myMap.begin(), ++myMap.begin() );//Erase with range
for( auto &ele: myMap)
{
if(ele.first ==1)
{
myMap.erase(ele.first);//erase by key
break; //You can't use ele again properly
//wthin this iteration, so break.
}
}
For std::list use std::list::erase()
markh44 is the most STL-ish response.
Note, however, that in general, iterators are invalidated by modifying the container, but set and map are exceptions. There, you can remove items and still go on using the iterators, except if you delete the very item your iterator is referencing.
Use the fact that the post-decrement operator returns a copy of the iterator before it decrements. Since the decremented iterator is still valid after erasing the current element, the for loop continues to operate as intended.
#include <list>
std::list<int> myList;
for(int i = 0; i < 10; ++i )
{
myList.push_back(i);
}
int cnt = 0;
for(std::list<int>::iterator iter = myList.begin(); iter != myList.end(); ++iter)
{
if( cnt == 5 )
{
myList.erase(iter--);
}
++cnt;
}
Edit: Doesn't work if you attempt to erase the first element in the list....
template <class Container, class Predicate>
void eraseIf( Container& container, Predicate predicate ) {
container.erase( remove_if( container.begin(), container.end(), predicate ), container.end() );
}
// pre-c++11 version
template<class K, class V, class Predicate>
void eraseIf( std::map<K,V>& container, Predicate predicate) {
typename std::map<K,V>::iterator iter = container.begin();
while(iter!=container.end()) {
iterator current = iter++;
if(predicate(*current))
container.erase(current);
}
}
// c++11 version
template<class K, class V, class Predicate>
void eraseIf( std::map<K,V>& container, Predicate predicate) {
auto iter = container.begin();
while(iter!=container.end()) {
if(predicate(*iter))
iter = container.erase(iter);
else
++iter;
}
}