How to erase elements from a vector of object? - c++

I know how we can remove elements from a vector of int
std::vector<int> vec;
// .. put in some values ..
int int_to_remove = n;
vec.erase(std::remove(vec.begin(), vec.end(), int_to_remove), vec.end());
What if its a vector<obj> vec where obj is
class obj {
int ID;
string name;
}
How would I remove vectors that are holding onto a certain ID ?
std::vector<obj> vec;
// .. put in some values ..
int id_to_remove = n;
vec.erase(std::remove(vec.ID.begin(), vec.ID.end(), id_to_remove), vec.end());

Now that you are looking to delete objects matching a certain criteria, you need to use std::remove_if instead of std::remove.
vec.erase(
std::remove_if(
vec.ID.begin()
, vec.ID.end()
, [](const obj& x) {
// ID needs to be public in order for this to compile
return x.ID == id_to_remove;
}
)
, vec.end()
);

Related

Remove and delete pointers that match a condition in a vector

I have a std::vector that I would like to remove pointers from the vector that meet the condition isDestroyed(), but also call delete on the pointers.
I did the following, but it requires looping over the vector twice. Is there a more efficient way to do it?
std::vector<GameObject*> gameObjects;
std::vector<GameObject*> destroyedObjects;
// Get objects to be deleted
std::copy_if (gameObjects.begin(), gameObjects.end(), std::back_inserter(destroyedObjects), [](GameObject* b){return b->isDestroyed();} );
// Remove objects from vector
gameObjects.erase(
std::remove_if(
gameObjects.begin(),
gameObjects.end(),
[](GameObject* p) { return p->isDestroyed(); }
),
gameObjects.end()
);
// Delete the objects
for (GameObject* o : destroyedObjects)
delete o;
std::unique_ptr does the deletion for free:
std::vector<std::unique_ptr<GameObject>> gameObjects;
// Remove objects from vector
gameObjects.erase(
std::remove_if(
gameObjects.begin(),
gameObjects.end(),
[](const auto& p) { return p->isDestroyed(); }
),
gameObjects.end()
);
I suggest to use it instead. It also avoids mistakes like forgetting to delete or double deleting.
This should work:
std::vector<GameObject*> gameObjects;
auto end = std::stable_partition(
gameObjects.begin(),
gameObjects.end(),
[](GameObject* p) { !return p->isDestroyed(); }
);
for (auto i = end; i < gameObjects.end(); i++) {
delete *i;
}
gameObjects.erase(end, gameObjects.end());
You don't need 2 vectors. The iterator returned by std::remove_if() can be used to know which objects need to be delete'd:
std::vector<GameObject*> gameObjects;
...
auto newEnd = std::remove_if(
gameObjects.begin(), gameObjects.end(),
[](GameObject* p) { return p->isDestroyed(); }
);
for(auto iter = newEnd; iter != gameObjects.end(); ++iter) {
delete *iter;
}
gameObjects.erase(newEnd, gameObjects.end());
If you change your vector to hold std::unique_ptr<GameObject> instead of GameObject*, you don't need to delete the objects manually anymore:
std::vector<std::unique_ptr<GameObject>> gameObjects;
...
gameObjects.erase(
std::remove_if(
gameObjects.begin(), gameObjects.end(),
[](std::unique_ptr<GameObject> &p) { return p->isDestroyed(); }
),
gameObjects.end()
);

Erase list element from unordered_set

I have a list<pair<int , double>> lSeedList and an unordered_set<int> sToDelete. I want to remove the pairs in the list that have their first member equal to an int in sToDelete. Currently I am using the following code :
void updateSL(list<pair<int, double> >& lSeedList, const unordered_set<int>& sAddedFacets)
{
list<pair<int, double> >::iterator it = lSeedList.begin();
while(it != lSeedList.end())
{
if(sAddedFacets.count(it->first) != 0)
it = lSeedList.erase(it);
else
++it;
}
}
Is there a way to speed up this code ? Is it possible to efficiently parallelize it with OpenMP (dividing the list in each thread and then merging them with splice) ?
I am using Visual Studio 2010 under Windows 7. The size of lSeedList is ~1 million at the start and the size of sToDelete is ~10000. The int in the pair acts like an unique ID.
It is better to use either standard algorithm std::remove_if
For exameple
lSeedList.erase( std::remove_if( lSeedList.begin(), lSeedList.end(),
[&]( const std::pair<int, double> &p )
{
return sAddedFacets.count( p.first );
} ),
lSeedList.end() );
Or member function remove_if of class std::list
For example
lSeedList.remove_if( [&]( const std::pair<int, double> &p )
{
return sAddedFacets.count( p.first );
} );

Implementation of lower_bound on vector pairs

I know we need to include some compare function in order to achieve this.
But not able to write for this one.
For example:
Elements of vector={(2,4),(4,2),(5,1),(5,3)}
to find=5
lower_bound() should return 2
code->
#define pp pair<int,int>
bool cmp(const pp &l,const pp &r) {
return l.first < r.first;
}
int main() {
vector<pp> v;
sort(v.begin(), v.end(), cmp);
int id=(int)(lower_bound(v.begin(), v.end(), ??) - v.begin());
}
Pairs (just like tuples) compare lexicographically anyway. You don't need to define any special comparators for this.
And since you're using lower_bound you'll be searching for the first element that does not compare less than the val you're searching, so you should use a min value as the second pair element. To sum up, all can be done in "two" lines of code :
sort(v.begin(),v.end());
auto id = distance(v.begin(), lower_bound(v.begin(),v.end(),
make_pair(5, numeric_limits<int>::min())) );
Some Notes :
Use std::distance to calculate the number of elements between two iterators
The return type of std::distance is an unsigned type. Unless you need negative indexing (Python like syntax for "count from the end" indexes) it's a good practice to keep your indexes unsigned.
Since you don't care about the second value of pp, just construct a temporary pp object with any value as the second element.
int id = std::lower_bound(v.begin(), v.end(), pp(5, 0), cmp) - v.begin();
I think you should compare the pairs as per definition of lower_bound
So,
typedef pair<int,int> pp;
//...
int id=(int)(lower_bound(v.begin(),v.end(),
pp(5,std::numeric_limits<int>::min())), //Value to compare
[](const pp& lhs, const pp& rhs) // Lambda
{
return lhs < rhs ; // first argument < second
}
) - v.begin()
);
You can use lower_bound on vector of pairs with custom compare operator .
You need to pass four arguments in that case like this :-
it1 = iterator position from where to search
it2 = iterator position till where to search
lower_bound (it1 ,it2 , finding_element, your_comparator )
auto myComp = [&](pair<int,string> e1, pair<int,string> e2) {
if(e1.second!=e2.second)
return e1.second<e2.second;
else
return e1.first<e2.first;
};
void Show_sample_code()
{
vector<pair<int,string>> data={{1, "sahil"}, {2, "amin"}};
sort(data.begin(), data.end(), myComp);
pair<int, string> p={1,"sahil"};
auto it=lower_bound( data.begin(), data.end(), p, myComp ) ;
if(it!=data.end())
cout<<"found at index="<<distance(data.begin(), it)<<endl;
else
cout<<"notfound"<<endl;
return;
}

My vector iterator is coming up with an odd error

I have an iterator for a vector that I want to delete certain elements in the vector. Running the code, no errors come up in the compiling, but after executing I receive a message saying 'Debug assertion failed!'. Could somebody tell me what's wrong with my iterator?
int function(unsigned int n, unsigned int m)
{
vector<int> vec1;
vec1.push_back(3);
vec1.push_back(4);
vec1.push_back(5);
vector<int>::iterator vec2;
for (vec2 = vec1.begin(); vec2 != vec1.end(); ++vec2)
{
if(*vec2 == 4)
{
vec1.erase(vec2);
}
}
return 0;
}
The erase method will invalid your iterator. You must write:
for (vec2 = vec1.begin(); vec2 != vec1.end(); )
{
if(*vec2 == 4)
vec2 = vec1.erase(vec2);
else
++vec2;
}
Note that you must increment vec2 only if the element is not erased. If you increment vec2 at each iteration, you will miss some elements, because erase returns an iterator that pointed on the element next to the erased one.
The problem is that you are deleting from the vector being iterated. Once you have deleted an object from the vector, all its iterators are invalidated; subsequent access to them is undefined behavior.
To fix this problem, you can use indexing, and start walking the vector from the end. This way you would be able to decrement the index on each iteration, regardless of whether you deleted an object or not.
Vector elements are stored in contiguous memory, so when you erase an element of a vector the tail elements (the ones after the erased elements) have to be "shifted", thus all iterators into that part of the vector are invalidated.
The usual way to delete all elements that are equal to some value is using standard algorithm std::remove along with member function erase For example
void function()
{
vector<int> vec1;
vec1.push_back(3);
vec1.push_back(4);
vec1.push_back(5);
vec1.erase( std::remove( vec1.begin(), vec1.end(), 4 ), vec1.end() );
}
if you want to delete only one element that is equal to 4 you could write
void function()
{
vector<int> vec1;
vec1.push_back(3);
vec1.push_back(4);
vec1.push_back(5);
auto it = std::find( vec1.begin(), vec1.end(), 4 );
if ( it != vec1.end() ) vec1.erase( it );
}
If you want to delete all elements that are equal to 4 using a loop you should write
void function()
{
vector<int> vec1;
vec1.push_back(3);
vec1.push_back(4);
vec1.push_back(5);
for ( auto it = vec1.begin(); it != vec1.end(); )
{
if ( *it == 4 ) it = vec1.erase( it );
else ++it;
}
}

Copying C++ Map into key and value vectors

I have a map and I want the first column i.e (*it).first to be pushed back into a vector then (*it)->second to be pushed back into another vector
Is this the best way to do it?
std::vector<std::string>test;
for ( it=mymap.begin() ; it != mymap.end(); it++ )
{
test.push_back((*it).first);
}
My other question is if i have a loop i.e
how would I insert all the integers i into (*it).first?
for(int i = 0; i < 10; i++)
{
// 1 - 10 will go in (*it).first
}
I want to have some integers in (*it).first and have associated values in (*it).second;
Use std::transform.
First define two functions key and value which take the pair of strings and return the first or second value, respectively.
#include <map>
#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator>
const std::string& key(const std::pair<std::string, std::string>& keyValue)
{
return keyValue.first;
}
const std::string& value(const std::pair<std::string, std::string>& keyValue)
{
return keyValue.second;
}
Then use std::transform from <algorithm> with the functions to transform the map into either a vector of keys or a vector of values.
int main()
{
using namespace std; // be explicit normally, trying to be brief here
map<string, string> contacts;
contacts["alice"] = "555-2701";
contacts["bob"] = "555-2702";
vector<string> keys(contacts.size());
vector<string> values(contacts.size());
transform(contacts.begin(), contacts.end(), keys.begin(), key);
transform(contacts.begin(), contacts.end(), values.begin(), value);
cout << "Keys:\n";
copy(keys.begin(), keys.end(), ostream_iterator<string>(cout, "\n"));
cout << "\n";
cout << "Values:\n";
copy(values.begin(), values.end(), ostream_iterator<string>(cout, "\n"));
return 0;
}
Output:
Keys:
alice
bob
Values:
555-2701
555-2702
Your first question, "how can I push the first column of my map into one vector and the 2nd column into another" is solved thus:
std::map<std::string, std::string> mymap;
std::vector<std::string> keys;
std::vector<std::string> values;
for ( std::map<std::string,std::string>::iterator it=mymap.begin() ; it != mymap.end(); ++it )
{
keys.push_back(it->first);
values.push_back(it->second);
}
Your second question, "how would insert all the integers i into (*it).first ?" is solved thus:
std::map<int, int> mymap2;
for(int i = 0; i < 10; i++)
{
// Insert default value into map
// This sets '(*it).first' to 'i' and
// '(*it).second' to a default value (in
// this case, 0).
mymap2[i];
}
or
std::map<int, int> mymap3;
for(int i = 0; i < 10; i++)
{
// Insert specified value into map
// this sets '(*it).first' to 'i', and
// '(*it).second' to the value returned from the function.
maymap3[i] = ChooseSpecificValue(i);
}
Well, it can be done with a simple loop:
for (auto const& p: mymap) {
vec1.push_back(p.first);
vec2.push_back(p.second);
}
Or using the std::transform algorithm, though it's quite verbose here:
std::transform(mymap.begin(), mymap.end(), std::back_inserter(vec1),
[](MyMap::const_reference p) { return p.first; });
Assuming you've declared your map as string key and value (ie map<string, string> mymap; then it would be like below, also assuming you've declare 'it' variable as map<string, string>::iterator it, etc:
std::vector<std::string> test;
std::vector<std::string> second;
std::map<string, string>::iterator it;
for ( it=mymap.begin() ; it != mymap.end(); it++ )
{
test.push_back((*it).first);
second.push_back((*it).second);
}
Not sure about your next question.
The first part of your question:
std::vector<std::string> test;
std::vector<std::string> test2; // assuming map is from string to string
for (it = mymap.begin(); it != mymap.end(); ++it)
{
test.push_back(it->first); // push first in one vector
test2.push_back(it->second); // push second in another vector
}
So, yes a simple for can do what you want.
The second part of your question:
Since you are updating the key of the map, you would need to remove it from the map and insert the changed one. So:
std::string first, second;
first = it->first;
second = it->second;
mymap.erase(it); // be careful with invalidating iterator
// change first
mymap[first] = second;
To change first by adding all integers i to it, that would really depend on the type of first. For example with a string, you may mean something like this:
ostringstream sout;
for (int i = 0; i < 10; ++i)
sout << (i?" ":"") << i;
first = sout.str();
Or if first is for example a set, you may mean something like this:
for (int i = 0; i < 10; ++i)
first.insert(i);
and my other question is if i have a loop i.e how would insert all the
integers i into (*it).first?
In the case of a std::map, you can't modify the iterator returned like that ... the key member (i.e., the first) in the std::map key/value pair data-structure is intentionally designated as a constant value, and is initialized to its constant value at the beginning of the key/value pair's lifetime in the std::map data-structure. If the keys weren't constant, you would end up creating havoc when you change the key, since the nodes in a std::map are suppose to be sorted by the keys. The second member of the key/value pair data-structure is the member that can be changed.
So if you want to insert a set of key/value pairs in a map, you could simply do the following:
std::map<int, int> mymap;
int some_other_value = 100;
for (int i=0; i < 10; i++)
{
mymap[i] = some_other_value++;
}
it here will be an iterator which will point to one of the position in map and at max have one first and second value for one iterator . At max you can have multiple key or same key holding same/different values depending on key/value combination.
As far as pushing the value in the vector for a key in map is concern you can do it in the same way you are pushing the key
std::vector<std::string>test;
std::vector<std::string>test2;
for ( it=mymap.begin() ; it != mymap.end(); it++ )
{
test.push_back((*it).first);
test2.push_back((*it).second);
}
Neways yours question is very unclear .
Just in case you want to deal with different data types in your map I would template a generic copy function:
template <class A, class B>
void mycopy(std::map<A, B>&m, std::list<A>& keys, std::list<B>& values) {
typename std::map<A, B>::iterator it;
for (it = m.begin(); it != m.end(); ++it) {
keys.push_back( (*it).first );
values.push_back( (*it).second );
}
}
Mixing it up:
std::map<int, std::string> mymap;
std::list<int> keys;
std::list<std::string> values;
mymap[1] = "string1";
mymap[2] = "string2";
mycopy(mymap, keys, values);
std::map<std::string, int> mymap1;
std::list<std::string> keys1;
std::list<int> values1;
mymap1["string1"] = 1;
mymap1["string2"] = 2;
mycopy(mymap1, keys1, values1);
Edit: yes __copy isnt the best definition. Thanks