3

In C++, the erase-remove idiom is a great way to delete all elements of a standard container that satisfy a given criterion.

Is it possible to extend the erase-remove idiom to work on multiple containers at once?

That is, can one invoke something similar to erase-remove on one container, and have the corresponding elements in another container removed as well?

In my particular case, the containers are all std::vectors of the same size.

For example, if elements 0, 3, and 5 are deleted from the first container, I would like elements 0, 3, and 5 deleted from the second container as well.

One could, for example, precompute a container that flags the elements to be deleted, build a predicate for remove_if that simply indexes into the flag container, and invoke erase-remove multiple times.

Is it possible to do what I want, without the precomputation?

peak
  • 105,803
  • 17
  • 152
  • 177

1 Answers1

-1

Yes you can do this. It works as is. I provide an example code and explain on it.

// initialises a vector that holds the numbers from 0-9.
  std::vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
  print(v);

  // removes all elements with the value 5
  v.erase( std::remove( v.begin(), v.end(), 5 ), v.end() ); 
  print(v); 

In this code we want to delete 5 from vector v and we use std::remove and std::erase afterwards. You have to understand what does std::remove do. std::remove swaps elements in container a way that all elements to be deleted go to the end and this function returns an iterator to the first element to be deleted. Next std::erase takes this iterator and removes all elements beginning from this iterator and till the end of a container (actually till the second argument. v.end() in our case).

std::vector<int> v1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
std::vector<int> v2 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 };

auto removeIt=std::remove( v1.begin(), v1.end(), [](const int value){
    return value==0 || value==3 || value==5;
} );

// now v1 = { 1, 2, 4, 6, 7, 8, 9, 0, 3, 5 } and removeIt points to 0 element (next after 9)..
                     // removeIt = ^

std::erase(std::remove_if(v2.begin(),v2.end(),[&](const int value){
    // remove every object that can be found between removeIt and v1.end()
    return std::find(removeIt,v1.end(),value)!=v1.end();
}), v2.end());
// now v2 = { 1, 2, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }
std::erase(removeIt,v1.end());
// now v1 = { 1, 2, 4, 6, 7, 8, 9 }

This code does exactly what you need.

fnc12
  • 2,241
  • 1
  • 21
  • 27
  • 1
    Why would you not use the predicate on the second container? Your second call can have O(n^2) complexity. I think the OP knows that he can just call `std::remove` twice. – Jens Feb 20 '16 at 09:07