Erase element by value from vector of pairs - c++

Since C++20, we can erase an element by value from a vector by doing, for example:
std::vector<int> v = {10,20,30,40,50};
std::erase(v,30);
That's really convenient and all not to mention there's also std::erase_if.
However, what if we have a vector of pairs and we want to erase, only if the second value of the pair matches?
std::pair<int, std::string> foo = std::make_pair(1,"1");
std::pair<int, std::string> foo2 = std::make_pair(2,"2");
std::vector< std::pair<int, std::string> > v;
v.push_back(foo);
v.push_back(foo2);
std::erase(v, make_pair(1,"2")); //This is not going to work!
So, is there a way to erase the element by the second value from a vector of pairs?

It would be something like:
std::erase_if(v, [](const auto& p){ return p.second == "2"; });
Demo

Related

Push_back into map<int,vector<char>>*

c++
map<int, vector>* maxcounts;
When I have a pointer to map maxcount how do I write this next statement correctly?
maxcounts[-m.second]->push_back(m.first);
without referencing a pointer I write
maxcounts[-m.second].push_back(m.first);
map<int, vector<char>> maxcounts;
for (pair<char, int> m : counts) {
if (maxcounts.count(-m.second))
maxcounts[-m.second].push_back(m.first);
else
maxcounts.insert({ -m.second, {m.first} });
}
To figure out how to use a pointer to the map, first rewrite your loop this way:
std::map<char, int> counts;
//...
std::map<int, std::vector<char>> maxcounts;
for (std::pair<char, int> m : counts)
maxcounts.insert({-m.second, std::vector<char>()}).first->second.push_back(m.first);
Note that the return value for std::map::insert is a std::pair, where the first of the pair is an iterator to the existing item if the item already is in the map, or the iterator to the newly inserted item. Thus you can perform the test and insert in one line without need for an if statement.
The push_back will occur, regardless of whether the item inserted in the map is new or if the item existed. Note that for a new entry, the std::vector being inserted starts as empty.
Given this, the pointer to the map version is very simple:
std::map<char, int> counts;
//...
map<int, vector<char>>* maxcounts;
//
for (pair<char, int> m : counts)
maxcounts->insert({-m.second, std::vector<char>()}).first->second.push_back(m.first);
Now, why you need a pointer to a map in the first place is another issue, but to answer your question, the above should work.
I would likely write something like:
std::map<int, std::vector<int>>* maxcounts = ...;
for (std::pair<char, int> m : counts)
(*maxcounts)[-m.second].push_back(m.first);

Is there any way in C++ to move item from one std::map to another with preserving the iterators?

here is I want to achieve:
Lets say I have 2 std::map<int,int> containers with items
m1 = {{1,1}, {2,2}, {3,3}};
m2 = {{4,4}, {5,5}};
And I have an iterator referencing to the second item from m1 ({2,2})
auto it = m1.find(2);
Now I want to move the item with key 2 from m1 to m2 and so the iterator it should refer to the right element inside the m2 without reassigning.
Before moving:
m1 = {{1,1}, {2,2}, {3,3}}
^
it
m2 = {{4,4}, {5,5}}
After moving:
m1 = {{1,1}, {3,3}}
m2 = {{2,2}, {4,4}, {5,5}}
^
it
So far I have wrote the code that do what I want:
std::map<int,int> m1 {{1,1}, {2,2}, {3,3}};
std::map<int,int> m2 {{4,4}, {5,5}};
auto it = m1.find(2);
m2.insert(std::move(m1.extract(m1.find(2))));
But the specification says that iterators referencing to the extracted items are invalidated.
So is it safe to use the iterator to the element after it was moved with extract method? Or is there any other way to achieve what I want?
I would appreciate any thoughts, thanks.
Not sure how you actually fill these maps, but rather naive solution which comes to my mind without any bigger context:
#include <iostream>
#include <vector>
using namespace std;
using MyCustomMap = std::vector<std::pair<int, int>*>;
std::pair<int, int>* findInMyCustomMap(const MyCustomMap& my_map, int key) {
for(const auto& i : my_map) {
if(i->first == key) {
return i;
}
}
}
int main()
{
std::pair<int, int> el1{1,1};
std::pair<int, int> el2{2,2};
std::pair<int, int> el3{3,3};
std::pair<int, int> el4{4,4};
std::pair<int, int> el5{5,5};
MyCustomMap m1{{&el1, &el2, &el3}};
MyCustomMap m2{{&el4, &el5}};
const auto it = findInMyCustomMap(m1, 2);
m2.insert(m2.begin(), it);
std::cout << it->second << std::endl;
}
https://onlinegdb.com/SJL1ma_xP
Please keep in mind the scope/life of the objects referenced, though.
std::map::insert() returns an iterator to the inserted element. There is no way to make your it variable magically start pointing from m1 to m2, especially since std::map::extract() "invalidates the iterators to the extracted element", so you MUST reassign it after the extraction, eg:
auto it = m1.find(2);
if (it != m1.end())
it = m2.insert(m1.extract(it)).position;
Alternatively:
auto it = m2.insert(m1.extract(2)).position;
Since you want it to point at the element moved into m2, you must use the new iterator that insert() returns to you. The old iterator has been invalidated and can no longer be used.

Push_back map into vector

How can I push_back a std::map element into std::vector?
std::vector<std::map<int, int>> v;
// Error
v.push_back(std::make_pair(0, 1))
What cause this error?
Using
v.push_back(std::make_pair(0, 1))
is a problem since a std::pair cannot be converted to a std::map. You can resolve the problem using one of the following methods that I can think of.
Method 1
If you can use a C++11 compiler,
v.push_back({{0, 1}});
Method 2
std::map<int, int> m;
m.insert(std::make_pair(0, 1));
v.push_back(m);
Method 3
std::map<int, int> m;
v.push_back(m);
v.back().insert(std::make_pair(0, 1));
std::vector<std::map<int, int>> v;
means you are declaring vector of maps!
push_back a map into vector, like this
v.push_back({std::make_pair(0, 1)}); //Needs C++11
OR
std::map<int, int> map1;
map1.insert(std::make_pair(0, 1));
v.push_back(map1);
push_back'ng a pair will obviously result into a compilation error.
You can push a map into your Data Structure, std::vector<std::map<int, int>> v. not a pair i.e. element of map.
So, do as following.
std::map<int,int> t;
t.insert(std::make_pair(0, 1));
v.push_back(t);
or in case of C++11
v.push_back({{1,2}});
Or alternatively you can go for emplace_back too.
void emplace_work_around(
std::vector<std::map<int, int>>& v,
std::initializer_list<std::pair<const int,int>> item
)
{
v.emplace_back(item);
}
int main()
{
std::vector<std::map<int, int>> v;
emplace_work_around(v,{{1,2}});
}

Getting all the keys of a map of the form <pair<int,int>, int*> in C++

In my C++ code I am using a map like this:
std::map<std::pair<int,int>,int*> patterns;
The problem is that I cannot figure out how I get all the keys of that map which are of the form
pair<int,int>
I have seen a few questions related to it, but in all the cases keys are single integers.
If you wanted to just iterate through all the keys:
C++03
for (std::map<std::pair<int,int>,int*>::iterator I = patterns.begin(); I != patterns.end(); I++) {
// I->first is a const reference to a std::pair<int,int> stored in the map
}
C++11
for (auto& kv : patterns) {
// kv.first is a const reference to a std::pair<int,int> stored in the map
}
If you wanted to copy the keys into a new container:
C++03
std::vector<std::pair<int,int> > V;
std::set<std::pair<int,int> > S;
for (std::map<std::pair<int,int>,int*>::iterator I = patterns.begin(); I != patterns.end(); I++) {
V.push_back(I->first);
S.insert(I->first);
}
C++11
std::vector<std::pair<int,int>> V;
std::set<std::pair<int,int>> S;
for (auto& kv : patterns) {
V.push_back(kv.first);
S.insert(kv.first);
}
Because I'm bored, here are a few additional solutions:
You could also do it with standard algorithms and a lambda function, but I don't think this is really better than just writing the loop yourself:
std::vector<std::pair<int,int>> V(patterns.size());
std::transform(patterns.begin(), patterns.end(), V.begin(),
[](decltype(patterns)::value_type& p){ return p.first; });
std::set<std::pair<int,int>> S;
std::for_each(patterns.begin(), patterns.end(),
[&S](decltype(patterns)::value_type& p){ S.insert(p.first); });
You could also use a Boost transform iterator to wrap iterators from the map, such that when the wrapped iterator is dereferenced, it gives you just the key from the map. Then you could call std::vector::insert or std::set::insert directly on a range of transform iterators.

Search in vector<std::pair<int, vector<int> > >

I would like to search within a vector<std::pair<int, vector<int> > >. This won't work due to the vector parameters:
std::vector<std::pair<int, std::vector<int> > > myVec;
iterator = find(myVec.begin(), myVec.end(), i);
Search would be on the first std::pair template parameter (int), in order to get the vector associated with it.
std::vector<std::pair<int, std::vector<int> > > myVec;
This requires C++0x for the lambda expression:
typedef std::pair<int, std::vector<int>> pair_type
std::find_if(myVec.begin(), myVec.end(), [i](pair_type const& pair)
{ return pair.first == i; });
If you're not using C++0x then either roll out your own loop or use something like Boost.Phoenix/Boost.Lambda.
Or, for both cases, why not use std::map?
You could make do with the following (pretty ugly) functoid:
struct FindFirst {
FindFirst(int i) : toFind(i) { }
int toFind;
bool operator()
( const std::pair<int, std::vector<int> > &p ) {
return p.first==toFind;
}
};
using it like this ( I couldn't get bind2nd to work - that's why I used the c'tor ):
int valueToFind = 4;
std::find_if(myVec.begin(), myVec.end(), FindFirst(valueToFind));
I think what you would like is a map:
std::map< int, vector< int > > foo;
You can then add elements, search by key etc:
int key = 4; //This will be the key
vector<int> value(5, 4); //Fill some values (5 4's in this case) into the vector
foo[key]=value; //Adds the pair to the map. Alternatively;
foo.insert( make_pair(key, value) ); //Does the same thing (in this context)
Looking at the way you've done things though, you might be wanting a std::multimap (which allows multiple values to have the same key) Class docs here
You're trying to map an int to a vector of int.
So try map<int, vector<int> >.
The second template parameter of a vector is the allocator - your compiler can probably puzzle out what you wanted to say, the declaration is wrong anyway. What you probably want is some sort of map type, like iammilind suggested.